phanerozoic commited on
Commit
60d3c95
·
1 Parent(s): fb69925

Store the host in a two-head machine of 4096 cells so that it reproduces itself by the dynamics, prove the machine's netlist for every window size, add figures and the layouts behind the digests, and remove the interpreter tower and the verification tiers

Browse files
README.md CHANGED
@@ -574,14 +574,18 @@ host, its initial memory and its own description tape; each generation is
574
  instantiated from the bytes the last one emitted, by parsing that output,
575
  rebuilding the netlist from its first field, and building the evaluator from
576
  that netlist. It also shows where the restriction to {-1, 0, 1} is paid for,
577
- that a fixed ternary network of 45,891 units, the two-head machine of
578
  `src/twohead_net.py`, holds a stored network in its state and evaluates one
579
- record per step, that an organism of 133 records on it translates a description
580
- of 2930 bits into the idle bank, copies the description and hands over, so that
581
- started on its own description it reproduces in every generation, the proofs of
582
- which are checked in Rocq in `rocq/`, that a larger record interpreter of
583
- `neural_reflect` runs a netlist realizing a smaller one, and that the
584
- constructor itself runs as a netlist stored in that interpreter's state. Its robustness section gives the margin every orbit has
 
 
 
 
585
  against perturbation, a certificate of the leakage an orbit tolerates from the
586
  inputs active along it, and the amplified host of `src/redundant.py`: every row
587
  of the levelized host replaced by r rows reading all r copies of its inputs,
@@ -590,17 +594,11 @@ wrong copies meet, with a probability of second order that the paper bounds, and
590
  completes the self-reproducing run under read noise at levels where the host
591
  fails within its first bytes.
592
 
593
- `python src/verify.py` audits every statement of the paper, and `--tier 2` adds
594
- every construction end to end; the checks of a tier run concurrently. `--tier 3` runs
595
- the self-reproducing instance on the threshold evaluators, the perturbation
596
- experiments and the amplified host, whose cost belongs to the object: one
597
- generation is 1,664,939 steps of a map of 41,967 units, and the amplified host
598
- reproduces itself in 33,796,449 steps of 125,901 units. The scripts that produce
599
- every figure are `src/selfrep.py`, `src/netlist_io.py`, `src/host_family.py`,
600
  `src/paper_stats.py`, `src/paper_verify.py`, `src/paper_runs.py`,
601
- `src/twohead.py`, `src/twohead_net.py`, `src/check_c.py`, `src/hierarchy.py`,
602
- `src/hosted_constructor.py`, `src/throughput.py`, `src/noise.py`,
603
- `src/redundant.py`, `src/fanin2.py`, `src/neuromorphic.py` and the
604
  `src/check_*.py` set, and their records are in `paper/runs/`; `rocq/` holds the
605
  Rocq development, which `src/check_rocq.py` builds.
606
 
@@ -995,7 +993,7 @@ src/ the library (run scripts as `python src/<nam
995
  │ program P*, the framing maps, and three evaluators
996
  ├── canonical.py deterministic safetensors writer (pinned key and tensor order)
997
  ├── paper_stats.py unit counts, levelization sizes, effective fan-in
998
- ├── paper_verify.py codec, framing, serialization, datapath, interpreter
999
  ├── paper_runs.py self-description, three generations, lockstep
1000
  ├── host_family.py the host family H_A: a constant-depth SUBLEQ step for
1001
  │ every word width, and its serialization in build order
@@ -1004,23 +1002,22 @@ src/ the library (run scripts as `python src/<nam
1004
  ├── check_c.py the host on the evaluator in C
1005
  ├── twohead.py the two-head machine, transcribed, the description code
1006
  │ and the translating organism; writes rocq/Organism.v
1007
- ├── twohead_net.py the two-head machine as a ternary netlist N_{G,L}: its
1008
- │ check, its file, the organism on it in C, and the
1009
- │ comparison with the netlist extracted from rocq/
1010
  ├── twohead_lockstep.c the organism on N_{G,L} beside the transcription, in C
 
 
 
 
1011
  ├── restore_bound.c the restoration bound of the amplified host along the orbit
1012
  ├── check_rocq.py builds rocq/ and prints the assumptions of its theorems
1013
  ├── independent/ separate code written from the paper's definitions: the host
1014
- │ family, the levelization, the record interpreter, a noise
1015
- │ simulator in C, weight error, and the neuromorphic datapath
1016
  ├── redundant.py the amplified host N_r: r-fold redundancy with the restoring
1017
  │ organ inside every unit, its self-reproduction, its runs
1018
  │ under read noise
1019
- ├── fanin2.py rewrites a netlist to effective fan-in two
1020
- ├── hierarchy.py a larger interpreter interpreting a netlist realizing a
1021
- │ smaller one
1022
- ├── hosted_constructor.py the universal constructor as a netlist stored in the
1023
- │ interpreter, with tape, output word and device in signals
1024
  ├── noise.py read noise on the self-reproducing run, the deviation curve
1025
  │ against the bounds, weight error with zero-entry leakage, the
1026
  │ active-input counts that certify an orbit
@@ -1030,8 +1027,9 @@ src/ the library (run scripts as `python src/<nam
1030
  framing, serialization, all 29 machines
1031
  paper/ the manuscript, and the record of every run it reports
1032
  rocq/ Rocq proofs: the netlist of the two-head machine computes its
1033
- step, the organism builds every image from any description
1034
- of it and reproduces in every generation
 
1035
  tools/ build_all.py (build + quantize + verify every profile),
1036
  cpu_programs.py (assembler + CPU program suite), test_cpu.py
1037
  (program suite vs a variant), play.py (interactive demo),
 
574
  instantiated from the bytes the last one emitted, by parsing that output,
575
  rebuilding the netlist from its first field, and building the evaluator from
576
  that netlist. It also shows where the restriction to {-1, 0, 1} is paid for,
577
+ and that a fixed ternary network, the two-head machine of
578
  `src/twohead_net.py`, holds a stored network in its state and evaluates one
579
+ record per step, which `rocq/` proves for every window of 2^α cells, bank and
580
+ tape. The host itself, compiled by `src/stored_host.py` into 36,074 records of
581
+ two inputs with records that load its memory, its tape device and a hand-over,
582
+ and stored in the member with 4096 cells, runs the 27-instruction program on a
583
+ recipe for its own records: it writes them into the idle bank, copies the recipe
584
+ and hands control to the copy, so that it reproduces itself, weights included,
585
+ by the dynamics alone; `src/stored_host_run.c` runs three generations of
586
+ 79,464,240,088 steps each. An organism of 133 records on the member with 64
587
+ cells does the same with a description code of its own, every generation of it
588
+ proved in Rocq. Its robustness section gives the margin every orbit has
589
  against perturbation, a certificate of the leakage an orbit tolerates from the
590
  inputs active along it, and the amplified host of `src/redundant.py`: every row
591
  of the levelized host replaced by r rows reading all r copies of its inputs,
 
594
  completes the self-reproducing run under read noise at levels where the host
595
  fails within its first bytes.
596
 
597
+ The scripts that produce every figure are `src/selfrep.py`, `src/netlist_io.py`, `src/host_family.py`,
 
 
 
 
 
 
598
  `src/paper_stats.py`, `src/paper_verify.py`, `src/paper_runs.py`,
599
+ `src/twohead.py`, `src/twohead_net.py`, `src/stored_host.py`,
600
+ `src/stored_host_run.c`, `src/check_c.py`, `src/throughput.py`, `src/noise.py`,
601
+ `src/redundant.py`, `src/neuromorphic.py` and the
602
  `src/check_*.py` set, and their records are in `paper/runs/`; `rocq/` holds the
603
  Rocq development, which `src/check_rocq.py` builds.
604
 
 
993
  │ program P*, the framing maps, and three evaluators
994
  ├── canonical.py deterministic safetensors writer (pinned key and tensor order)
995
  ├── paper_stats.py unit counts, levelization sizes, effective fan-in
996
+ ├── paper_verify.py codec, framing, serialization, datapath, levelization
997
  ├── paper_runs.py self-description, three generations, lockstep
998
  ├── host_family.py the host family H_A: a constant-depth SUBLEQ step for
999
  │ every word width, and its serialization in build order
 
1002
  ├── check_c.py the host on the evaluator in C
1003
  ├── twohead.py the two-head machine, transcribed, the description code
1004
  │ and the translating organism; writes rocq/Organism.v
1005
+ ├── twohead_net.py the two-head machine as a ternary netlist N_{G,L} for a window
1006
+ │ of 2^α cells: its check, its file, the organism on it in C,
1007
+ │ and the comparison with the netlist extracted from rocq/
1008
  ├── twohead_lockstep.c the organism on N_{G,L} beside the transcription, in C
1009
+ ├── stored_host.py the host compiled into 36,074 records of the two-head machine
1010
+ │ with 4096 cells: one pass against the host's step and device,
1011
+ │ the image and its recipe, and its generations
1012
+ ├── stored_host_run.c the stored host's generations on the two-head machine, in C
1013
  ├── restore_bound.c the restoration bound of the amplified host along the orbit
1014
  ├── check_rocq.py builds rocq/ and prints the assumptions of its theorems
1015
  ├── independent/ separate code written from the paper's definitions: the host
1016
+ │ family, the levelization, a noise simulator in C, weight
1017
+ │ error, and the neuromorphic datapath
1018
  ├── redundant.py the amplified host N_r: r-fold redundancy with the restoring
1019
  │ organ inside every unit, its self-reproduction, its runs
1020
  │ under read noise
 
 
 
 
 
1021
  ├── noise.py read noise on the self-reproducing run, the deviation curve
1022
  │ against the bounds, weight error with zero-entry leakage, the
1023
  │ active-input counts that certify an orbit
 
1027
  framing, serialization, all 29 machines
1028
  paper/ the manuscript, and the record of every run it reports
1029
  rocq/ Rocq proofs: the netlist of the two-head machine computes its
1030
+ step for every window of 2^α cells, the organism builds every
1031
+ image from any description of it and reproduces in every
1032
+ generation
1033
  tools/ build_all.py (build + quantize + verify every profile),
1034
  cpu_programs.py (assembler + CPU program suite), test_cpu.py
1035
  (program suite vs a variant), play.py (interactive demo),
paper/README.md CHANGED
@@ -9,37 +9,11 @@ universal_construction_ternary.pdf typeset
9
  runs/ the record written by each script
10
  ```
11
 
12
- ## Reproducing it
13
-
14
- ```bash
15
- python src/verify.py # the statements
16
- python src/verify.py --tier 2 # and every construction, end to end
17
- python src/verify.py --tier 3 # and the full-scale runs
18
- ```
19
-
20
- Tier one is an exhaustive or near-exhaustive audit of every definition, lemma,
21
- proposition and theorem, and none of its checks runs the full instance. Tier two
22
- executes every construction end to end: the self-describing instance and three
23
- generations of the self-reproducing instance on the integer reference, the
24
- constructor on four machines of the family through the netlist that
25
- `variants/neural_subleq8io_netlist.safetensors` denotes, the record interpreters
26
- of Section 9, the translating organism on the two-head machine and on its
27
- netlist in C, and the Rocq development of Sections 7 and 8, which needs Rocq 9.1
28
- and ocamlfind on the PATH. The two together take about forty minutes and cover
29
- every claim the paper makes.
30
 
31
- Tier three runs the self-reproducing instance itself on the threshold
32
- evaluators, runs the constructor on all twenty-nine machines of the artifact,
33
- carries out the perturbation experiments and the orbit certificate, and runs
34
- the amplified host: its exact self-reproduction on the evaluator in C, the
35
- self-reproducing instance of the host on it under read noise, its deviation
36
- curve, and the correction of single wrong copies. Its cost belongs to the object and not to the implementation: one
37
- generation is 1,664,939 steps of a map of 41,967 units, that is 6.99e10 unit
38
- evaluations, and the amplified host reproduces itself in 33,796,449 steps of
39
- 125,901 units. `runs/paper_throughput.json` holds the rate of each evaluator, so
40
- the cost of any one run is a multiplication.
41
 
42
- ## What each script establishes
43
 
44
  | Script | Record | Establishes |
45
  |---|---|---|
@@ -61,22 +35,19 @@ the cost of any one run is a multiplication.
61
  | `src/check_ternarity.py` | `paper_ternarity.json` | no ternary unit computes the branch predicate |
62
  | `src/check_environment.py` | `paper_environment.json` | the cost of the environment against the cost of the network |
63
  | `src/check_sigma.py` | — | sigma is a function, injective, and inverted by the parser |
64
- | `src/fanin2.py` | `paper_fanin2.json` | the fan-in-two rewriting, on the host, the interpreter and every small ternary unit |
65
  | `src/check_family.py` | `paper_runs_family.json` | the constructor emits all 29 machines |
66
  | `src/check_family_gate.py` | `paper_runs_family_gate.json` | four of them on the netlist of sigma |
67
- | `src/check_interp.py` | `paper_interp.json` | the record interpreter's netlist against its definition |
68
  | `src/check_c.py` | `paper_c_evaluator.json` | the host on the evaluator in C, `src/netlist_eval.c` |
69
  | `src/check_host_fold.py` | `paper_host_fold.json` | the folded host family, depth 11, against SUBLEQ_A for A = 3 to 12 |
70
  | `src/twohead.py` | `paper_twohead.json` | the translating organism on the two-head machine, three generations |
71
- | `src/twohead_net.py` | `paper_twohead_net.json` | the netlist N_{G,L} against the machine, its file, the organism on it in C, and the netlist extracted from Rocq |
72
- | `src/check_rocq.py` | `paper_rocq.json` | the Rocq development of `rocq/`: the netlist computes the step, construction and self-reproduction by the dynamics |
73
- | `src/hierarchy.py` | `paper_hierarchy.json` | a larger interpreter runs a smaller one transition by transition |
74
- | `src/hosted_constructor.py` | `paper_hosted.json` | the constructor as a netlist stored in the interpreter |
75
  | `src/throughput.py` | `paper_throughput.json` | the rate of each evaluator |
76
  | `src/noise.py` | `paper_noise_*.json` | read noise, the deviation curve, weight error with leakage, the active-input counts of the orbit certificate |
77
  | `src/redundant.py` | `paper_redundant_*.json` | the amplified host N_3: one step against SUBLEQ on replicated states, the margin 3/2, exact self-reproduction in C, the run under noise, the deviation curve and its bound by layer, the failure rate of the state read by majority and the restoration bound, computed by `src/restore_bound.c` |
78
  | `src/neuromorphic.py` | `paper_neuromorphic.json` | the datapath on the Loihi and Akida neuron models |
79
- | `src/independent/*.py`, `nsim.c` | `independent_*.json` | separate code written from the paper's definitions: H_A rebuilt from Construction 3.8 for A = 3 to 12, Lev(N_host) from sigma, Definition 9.1 with the hierarchy and the hosted constructor, Tables 4 to 7 with a separate noise simulator, and the datapath on Loihi and Akida |
80
 
81
  `src/neuromorphic.py run` needs Lava, which requires Python 3.10; everything
82
  else runs in the environment of the repository.
 
9
  runs/ the record written by each script
10
  ```
11
 
12
+ ## What each script establishes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
+ Each script runs on its own and writes its record to `runs/`; `src/check_rocq.py`
15
+ needs Rocq 9.1 and ocamlfind on the PATH.
 
 
 
 
 
 
 
 
16
 
 
17
 
18
  | Script | Record | Establishes |
19
  |---|---|---|
 
35
  | `src/check_ternarity.py` | `paper_ternarity.json` | no ternary unit computes the branch predicate |
36
  | `src/check_environment.py` | `paper_environment.json` | the cost of the environment against the cost of the network |
37
  | `src/check_sigma.py` | — | sigma is a function, injective, and inverted by the parser |
 
38
  | `src/check_family.py` | `paper_runs_family.json` | the constructor emits all 29 machines |
39
  | `src/check_family_gate.py` | `paper_runs_family_gate.json` | four of them on the netlist of sigma |
 
40
  | `src/check_c.py` | `paper_c_evaluator.json` | the host on the evaluator in C, `src/netlist_eval.c` |
41
  | `src/check_host_fold.py` | `paper_host_fold.json` | the folded host family, depth 11, against SUBLEQ_A for A = 3 to 12 |
42
  | `src/twohead.py` | `paper_twohead.json` | the translating organism on the two-head machine, three generations |
43
+ | `src/twohead_net.py` | `paper_twohead_net.json` | the netlist N_{G,L} against the machine for windows of 64 and 4096 cells, its file, the organism on it in C, and the netlist extracted from Rocq |
44
+ | `src/stored_host.py`, `src/stored_host_run.c` | `paper_stored_host.json` | the host compiled into records of the two-head machine: one pass against the host's step and its device, the image and its recipe, three generations in C |
45
+ | `src/check_rocq.py` | `paper_rocq.json` | the Rocq development of `rocq/`: the netlist computes the step for every window, construction and self-reproduction by the organism |
 
46
  | `src/throughput.py` | `paper_throughput.json` | the rate of each evaluator |
47
  | `src/noise.py` | `paper_noise_*.json` | read noise, the deviation curve, weight error with leakage, the active-input counts of the orbit certificate |
48
  | `src/redundant.py` | `paper_redundant_*.json` | the amplified host N_3: one step against SUBLEQ on replicated states, the margin 3/2, exact self-reproduction in C, the run under noise, the deviation curve and its bound by layer, the failure rate of the state read by majority and the restoration bound, computed by `src/restore_bound.c` |
49
  | `src/neuromorphic.py` | `paper_neuromorphic.json` | the datapath on the Loihi and Akida neuron models |
50
+ | `src/independent/*.py`, `nsim.c` | `independent_*.json` | separate code written from the paper's definitions: H_A rebuilt from Construction 3.8 for A = 3 to 12, Lev(N_host) from sigma, Tables 3 to 6 with a separate noise simulator, and the datapath on Loihi and Akida |
51
 
52
  `src/neuromorphic.py run` needs Lava, which requires Python 3.10; everything
53
  else runs in the environment of the repository.
paper/runs/independent_interpreter.json DELETED
@@ -1,40 +0,0 @@
1
- {
2
- "hierarchy": {
3
- "small_units": 816,
4
- "small_state_bits": 131,
5
- "fanin2_units": 1403,
6
- "signals": 1534,
7
- "changed": 35,
8
- "records": 1438,
9
- "big": {
10
- "A": 17,
11
- "G": 2088,
12
- "b": 62,
13
- "span": 1616
14
- },
15
- "states": 48,
16
- "passes": 4,
17
- "disagree": 0,
18
- "seconds": 5.6
19
- },
20
- "hosted": {
21
- "records": 494,
22
- "noops": 41,
23
- "tail_noops": true,
24
- "decode_matches": true,
25
- "recipes": 41,
26
- "recipes_ok": 41,
27
- "first": {
28
- "recipe_len": 9,
29
- "out_len": 12,
30
- "steps": 83,
31
- "passes": 83,
32
- "applications": 44358,
33
- "ok": true
34
- },
35
- "random_states": 2000,
36
- "random_halting_steps": 297,
37
- "random_disagree": 0,
38
- "seconds": 20.4
39
- }
40
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paper/runs/paper_fanin2.json DELETED
@@ -1,27 +0,0 @@
1
- {
2
- "small_units": 664,
3
- "small_unit_failures": 0,
4
- "netlists": {
5
- "N_host": {
6
- "units": 17598,
7
- "units_fanin2": 33568,
8
- "shapes_above_two": {
9
- "and": 821,
10
- "or": 70,
11
- "threshold": 15
12
- },
13
- "agrees_on_random_inputs": true
14
- },
15
- "N_U": {
16
- "units": 13034,
17
- "units_fanin2": 40081,
18
- "shapes_above_two": {
19
- "and": 3091,
20
- "or": 43
21
- },
22
- "agrees_on_random_inputs": true
23
- }
24
- },
25
- "seconds": 7.610988596003153,
26
- "ok": true
27
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paper/runs/paper_hierarchy.json DELETED
@@ -1,33 +0,0 @@
1
- {
2
- "small": {
3
- "S": 128,
4
- "G": 3,
5
- "units": 816,
6
- "units_fanin2": 1403,
7
- "signals": 1534,
8
- "state_bits": 131
9
- },
10
- "big": {
11
- "S": 131072,
12
- "G": 2088,
13
- "b": 62,
14
- "span": 1616,
15
- "units": 148571,
16
- "layers": 65,
17
- "Q_bits": 129456
18
- },
19
- "records": 1438,
20
- "copy_records": 35,
21
- "states": 48,
22
- "pass_steps": 2088,
23
- "transitions": 4,
24
- "mismatches_per_transition": [
25
- 0,
26
- 0,
27
- 0,
28
- 0
29
- ],
30
- "exact": true,
31
- "mismatched_coordinates": 0,
32
- "seconds": 62.78045246200054
33
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paper/runs/paper_hosted.json DELETED
@@ -1,27 +0,0 @@
1
- {
2
- "records": 494,
3
- "record_semantics_ok": true,
4
- "S": 32768,
5
- "G": 535,
6
- "b": 56,
7
- "span": 2808,
8
- "units": 59348,
9
- "layers": 61,
10
- "memory_bytes": 128,
11
- "tape_bytes": 9,
12
- "target_bytes": 12,
13
- "target": "ABCCCCCCCCDE",
14
- "instructions": 83,
15
- "reference_instructions": 83,
16
- "interpreter_steps": 44405,
17
- "emitted": "ABCCCCCCCCDE",
18
- "exact": true,
19
- "memory_matches_reference": true,
20
- "head": 9,
21
- "random_states_records": 2000,
22
- "random_states_records_wrong": 0,
23
- "random_states_netlist": 256,
24
- "random_states_netlist_wrong": 0,
25
- "seconds": 157.594675832981,
26
- "ok": true
27
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paper/runs/paper_interp.json DELETED
@@ -1,20 +0,0 @@
1
- {
2
- "members": [
3
- {
4
- "A": 10,
5
- "S": 1024,
6
- "G": 11,
7
- "banks": 2,
8
- "units": 13034,
9
- "fields": 576,
10
- "fields_wrong": 0,
11
- "random": 4096,
12
- "random_wrong": 0,
13
- "halted_not_fixed": 0,
14
- "counter_wrong": 0,
15
- "ok": true,
16
- "seconds": 7.045043751000776
17
- }
18
- ],
19
- "ok": true
20
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paper/runs/paper_lev.json CHANGED
@@ -7,8 +7,5 @@
7
  "nonternary_layers": 0,
8
  "noninteger_bias_layers": 0,
9
  "min_margin": 0.5,
10
- "max_noninteger": 0.0,
11
- "u_states": 64,
12
- "u_gates": 13034,
13
- "u_agree": true
14
  }
 
7
  "nonternary_layers": 0,
8
  "noninteger_bias_layers": 0,
9
  "min_margin": 0.5,
10
+ "max_noninteger": 0.0
 
 
 
11
  }
paper/runs/paper_rocq.json CHANGED
@@ -1,10 +1,12 @@
1
  {
2
  "rocq": "The Rocq Prover, version 9.1.1",
3
  "files": 25,
4
- "build_seconds": 220.6,
5
  "theorems": {
6
  "PhysMain.netlist_step": "closed",
 
7
  "PhysFacts.phys_size": "closed",
 
8
  "PhysFacts.phys_ternary": "closed",
9
  "NetMain.network_orbit": "closed",
10
  "NetMain.network_reproduces": "closed",
 
1
  {
2
  "rocq": "The Rocq Prover, version 9.1.1",
3
  "files": 25,
4
+ "build_seconds": 233.8,
5
  "theorems": {
6
  "PhysMain.netlist_step": "closed",
7
+ "PhysMain.netlist_step64": "closed",
8
  "PhysFacts.phys_size": "closed",
9
+ "PhysFacts.phys_size64": "closed",
10
  "PhysFacts.phys_ternary": "closed",
11
  "NetMain.network_orbit": "closed",
12
  "NetMain.network_reproduces": "closed",
paper/runs/paper_stats.json CHANGED
@@ -128,105 +128,5 @@
128
  "128": 8,
129
  "256": 40
130
  }
131
- },
132
- "lev_U": {
133
- "depth": 53,
134
- "gates": 13002,
135
- "netlist_gates": 13034,
136
- "state_bits": 1029,
137
- "widths": [
138
- 1029,
139
- 1948,
140
- 1957,
141
- 1500,
142
- 1487,
143
- 1074,
144
- 1104,
145
- 1126,
146
- 1096,
147
- 1099,
148
- 1093,
149
- 1096,
150
- 1090,
151
- 1093,
152
- 1087,
153
- 1090,
154
- 1084,
155
- 1087,
156
- 1081,
157
- 1084,
158
- 1078,
159
- 1081,
160
- 1075,
161
- 1078,
162
- 1072,
163
- 1072,
164
- 1069,
165
- 4111,
166
- 4111,
167
- 3089,
168
- 2065,
169
- 2075,
170
- 2071,
171
- 2071,
172
- 2069,
173
- 2070,
174
- 2067,
175
- 2068,
176
- 2064,
177
- 2066,
178
- 2062,
179
- 2063,
180
- 2060,
181
- 2059,
182
- 2057,
183
- 2057,
184
- 2056,
185
- 2056,
186
- 2055,
187
- 1031,
188
- 1030,
189
- 1039,
190
- 1038,
191
- 1029
192
- ],
193
- "M": 86490,
194
- "dense_entries": 160865677,
195
- "identity_units": 73488,
196
- "new_units": 13002,
197
- "max_fanin_netlist": 1024,
198
- "nonzero_weights_netlist": 51557,
199
- "bias_min": -10,
200
- "bias_max": 1,
201
- "fanin_hist": {
202
- "1": 1494,
203
- "2": 8374,
204
- "3": 1,
205
- "4": 12,
206
- "5": 1,
207
- "6": 1,
208
- "7": 1,
209
- "8": 1,
210
- "9": 1,
211
- "10": 3073,
212
- "11": 41,
213
- "1024": 2
214
- }
215
- },
216
- "U_cfg": {
217
- "S": 1024,
218
- "G": 11,
219
- "F": 2,
220
- "BB": 4,
221
- "banks": 2,
222
- "R": 41,
223
- "ACC": 6,
224
- "SLOT": 13,
225
- "A": 10,
226
- "NET0": 122,
227
- "NET1": 573,
228
- "WORK_BASE": 16,
229
- "span": 1024,
230
- "STATE_BITS": 1029
231
  }
232
  }
 
128
  "128": 8,
129
  "256": 40
130
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  }
132
  }
paper/runs/paper_stored_host.json ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "run": {
3
+ "records": 36074,
4
+ "window_cells": 2896,
5
+ "record_bits": 44,
6
+ "bank_bits": 1587256,
7
+ "image_bytes": 198407,
8
+ "tau_bytes": 199971,
9
+ "passes_expected": 2202812,
10
+ "generations": [
11
+ {
12
+ "generation": 1,
13
+ "steps": 79464240088,
14
+ "passes": 2202812,
15
+ "kappa": 1,
16
+ "bank_holds_image": 1,
17
+ "tape_holds_tau": 1,
18
+ "heads": [
19
+ 0,
20
+ 0,
21
+ 0,
22
+ 0
23
+ ],
24
+ "window_fnv": "d17d72606006fc62"
25
+ },
26
+ {
27
+ "generation": 2,
28
+ "steps": 79464240088,
29
+ "passes": 2202812,
30
+ "kappa": 0,
31
+ "bank_holds_image": 1,
32
+ "tape_holds_tau": 1,
33
+ "heads": [
34
+ 0,
35
+ 0,
36
+ 0,
37
+ 0
38
+ ],
39
+ "window_fnv": "e3a2c10c6eddcef2"
40
+ },
41
+ {
42
+ "generation": 3,
43
+ "steps": 79464240088,
44
+ "passes": 2202812,
45
+ "kappa": 1,
46
+ "bank_holds_image": 1,
47
+ "tape_holds_tau": 1,
48
+ "heads": [
49
+ 0,
50
+ 0,
51
+ 0,
52
+ 0
53
+ ],
54
+ "window_fnv": "d17d72606006fc62"
55
+ }
56
+ ],
57
+ "seconds": 534.3,
58
+ "sha256_image": "7a769aa264a89860149962d7e98f8b2a5cb34ceb7778e88bcbffdccdc0996154",
59
+ "sha256_tau": "fb23d9b4b41cd5d0576bce37d0cd53a0f63f33de6d4895f2e784eb1148387185",
60
+ "state_after_3_equals_after_1": 1,
61
+ "every_generation_exact": true
62
+ },
63
+ "pass_check": {
64
+ "trials": 300,
65
+ "disagreements": 0,
66
+ "records": 36074,
67
+ "window_cells": 2896,
68
+ "seconds": 131.8
69
+ }
70
+ }
paper/runs/paper_twohead_net.json CHANGED
@@ -12,13 +12,26 @@
12
  "states": 20,
13
  "disagreements": 0
14
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  "netlist": {
16
  "units": 45891,
17
  "edges": 99602,
18
  "depth": 15,
19
  "state_bits": 12837
20
  },
21
- "seconds": 4.9
22
  },
23
  "netlist": {
24
  "path": "variants/twohead_physics.bin",
@@ -42,10 +55,24 @@
42
  "seconds": 322.4
43
  },
44
  "extraction": {
45
- "N": 3458,
46
- "L": 2930,
47
- "identical": true,
48
- "sha256_generator": "0b86fbe2da1d67beafb252a445738794f4eb35ad4aa8987030c65d3612aa50fe",
49
- "sha256_rocq": "0b86fbe2da1d67beafb252a445738794f4eb35ad4aa8987030c65d3612aa50fe"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  }
51
  }
 
12
  "states": 20,
13
  "disagreements": 0
14
  },
15
+ "wide": {
16
+ "alpha": 12,
17
+ "G": 5,
18
+ "L": 40,
19
+ "states": 40,
20
+ "disagreements": 0,
21
+ "netlist": {
22
+ "units": 34957,
23
+ "edges": 201434,
24
+ "depth": 15,
25
+ "state_bits": 4613
26
+ }
27
+ },
28
  "netlist": {
29
  "units": 45891,
30
  "edges": 99602,
31
  "depth": 15,
32
  "state_bits": 12837
33
  },
34
+ "seconds": 2.3
35
  },
36
  "netlist": {
37
  "path": "variants/twohead_physics.bin",
 
55
  "seconds": 322.4
56
  },
57
  "extraction": {
58
+ "members": [
59
+ {
60
+ "alpha": 6,
61
+ "N": 3458,
62
+ "L": 2930,
63
+ "identical": true,
64
+ "sha256_generator": "0b86fbe2da1d67beafb252a445738794f4eb35ad4aa8987030c65d3612aa50fe",
65
+ "sha256_rocq": "0b86fbe2da1d67beafb252a445738794f4eb35ad4aa8987030c65d3612aa50fe"
66
+ },
67
+ {
68
+ "alpha": 12,
69
+ "N": 220,
70
+ "L": 40,
71
+ "identical": true,
72
+ "sha256_generator": "dc3b111811b5f3708641883c2313ff2d40387e4e288cb4a790a7289a0d8fcf4d",
73
+ "sha256_rocq": "dc3b111811b5f3708641883c2313ff2d40387e4e288cb4a790a7289a0d8fcf4d"
74
+ }
75
+ ],
76
+ "identical": true
77
  }
78
  }
paper/runs/paper_verify.json CHANGED
@@ -39,25 +39,11 @@
39
  "counter_pairs": 65536,
40
  "counter_failures": 0
41
  },
42
- "interpreter": {
43
- "onerecord_cases": 576,
44
- "onerecord_failures": 0,
45
- "counter_failures": 0,
46
- "Q_bits": 451,
47
- "influencing": 451,
48
- "S": 1024,
49
- "G": 11,
50
- "R": 41,
51
- "b": 41,
52
- "Q_size": 451,
53
- "records_max": 11,
54
- "units_lower_bound": 226
55
- },
56
  "lev": {
57
  "sparse_dense_agree": true,
58
  "layers": 13,
59
  "max_width": 8210,
60
  "total_weights": 124165940
61
  },
62
- "seconds": 282.2581712300016
63
  }
 
39
  "counter_pairs": 65536,
40
  "counter_failures": 0
41
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  "lev": {
43
  "sparse_dense_agree": true,
44
  "layers": 13,
45
  "max_width": 8210,
46
  "total_weights": 124165940
47
  },
48
+ "seconds": 190.45070967001084
49
  }
paper/runs/paper_verify_times.json DELETED
@@ -1,75 +0,0 @@
1
- {
2
- "tier": 1,
3
- "total_seconds": 214.8219217349979,
4
- "failures": 0,
5
- "checks": {
6
- "check_device.py": {
7
- "ok": true,
8
- "seconds": 0.14191210998978931
9
- },
10
- "check_framing.py": {
11
- "ok": true,
12
- "seconds": 0.5073039440030698
13
- },
14
- "twohead_net.py check": {
15
- "ok": true,
16
- "seconds": 5.247474653995596
17
- },
18
- "check_steps.py": {
19
- "ok": true,
20
- "seconds": 5.269182780990377
21
- },
22
- "check_optimal.py": {
23
- "ok": true,
24
- "seconds": 7.710004391003167
25
- },
26
- "check_codec.py": {
27
- "ok": true,
28
- "seconds": 7.851285790995462
29
- },
30
- "check_sigma.py": {
31
- "ok": true,
32
- "seconds": 11.03636751799786
33
- },
34
- "fanin2.py": {
35
- "ok": true,
36
- "seconds": 12.635428726993268
37
- },
38
- "check_environment.py": {
39
- "ok": true,
40
- "seconds": 12.856029444010346
41
- },
42
- "check_interp.py": {
43
- "ok": true,
44
- "seconds": 13.257643950011698
45
- },
46
- "check_host.py": {
47
- "ok": true,
48
- "seconds": 14.019029688002774
49
- },
50
- "check_ternarity.py": {
51
- "ok": true,
52
- "seconds": 15.632652968997718
53
- },
54
- "check_host_family.py": {
55
- "ok": true,
56
- "seconds": 26.31405289299437
57
- },
58
- "check_lev.py": {
59
- "ok": true,
60
- "seconds": 32.38079876700067
61
- },
62
- "check_host_fold.py": {
63
- "ok": true,
64
- "seconds": 51.92139666900039
65
- },
66
- "check_total.py": {
67
- "ok": true,
68
- "seconds": 205.5435071989923
69
- },
70
- "redundant.py build": {
71
- "ok": true,
72
- "seconds": 214.77837346300657
73
- }
74
- }
75
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paper/universal_construction_ternary.pdf CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:d05a260a7e12935b1e20bb7bc6e5690f2f29d9c04de210aaa7eb3618ab4280e1
3
- size 356867
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:29a8e26ce2577a85810fc815acc5eeaef1925cbaadb629aee163c64bf7cfdcf4
3
+ size 364771
paper/universal_construction_ternary.tex CHANGED
@@ -7,7 +7,12 @@
7
  \usepackage{booktabs,tabularx,array}
8
  \usepackage{cite}
9
  \usepackage{enumitem}
 
 
 
 
10
  \usepackage[margin=1in]{geometry}
 
11
  \usepackage[colorlinks=true,linkcolor=blue,citecolor=blue,urlcolor=blue]{hyperref}
12
 
13
  % ---- theorem environments ---------------------------------------------------
@@ -62,30 +67,28 @@ We study recurrent networks of McCulloch--Pitts threshold units with weights in
62
  $\{-1,0,1\}$ and integer thresholds, iterated as a fixed composition of ternary
63
  matrix--vector products and Heaviside steps, and construct networks of this kind
64
  that carry out von Neumann's scheme of universal construction and
65
- self-reproduction exactly. A SUBLEQ computer of $17{,}598$ units, one member of a
66
- family whose step has depth $13$ at every word width, runs a program of $21$
67
- instructions that emits any byte string from a run-length description, and one
68
- of $27$ instructions whose output is the serialization of every bias and edge of
69
- the machine with its memory and its tape. This is the standard quine scheme run
70
- on a universal machine, and a fixed instantiation map of linear cost rebuilds
71
- the running instance from the output. In a second construction the dynamics
72
- builds the offspring. A fixed network of $45{,}891$ units holds a stored network
73
- in its state, two banks of records and two tapes under three heads, and
74
- evaluates one record per step; an organism of $133$ records translates a
75
- description of $2930$ bits into the idle bank, copies the description and hands
76
- over control. Proofs checked in Rocq show that the network computes the step of
77
- this machine from every state at every size, that the organism builds every
78
- image of its size from any description of it, and that started on its own
79
- description it reproduces in every generation. Because every pre-activation is
80
- an integer, every orbit survives perturbations below $1/2$; a bound computed from
81
- the pre-activation profile is within $20\%$ of the measured failure rate under
82
- Gaussian read noise, and the inputs active along an orbit certify $23.5$ times
83
- the leakage the worst case allows over the first $10{,}000$ steps and $18.6$
84
- times over the whole run. Replacing each unit by $r$ copies that read every copy
85
- of their inputs multiplies every margin by $r$, so any noise level is survived
86
- with redundancy $O(s\sqrt{\log(TM/\delta)})$, and the threefold host completes
87
- the self-reproducing run at noise levels at which the host fails within its
88
- first bytes.
89
  \end{abstract}
90
 
91
  \maketitle
@@ -115,15 +118,17 @@ $F\colon\B^n\to\B^n$ built from ternary matrices and the Heaviside step, and its
115
  behaviour is the orbit of an initial state. The paper treats two questions.
116
  The first is whether such an orbit can emit, exactly, a serialization of the
117
  weights that define $F$ together with the rest of the initial data, so that
118
- loading the emitted data yields a process that emits the same data. The second
119
- is whether the offspring can be built by the dynamics: whether a fixed network
120
- can hold threshold networks in its state as data, and whether a network so held
121
- can construct another from a description, copy the description, and hand over
122
- control to what it built. The first is the
123
  setting of program replicators, which rely on an execution environment to
124
- instantiate what they emit~\cite{CotlerHonglerHudcova2025}; the second is the
125
- setting of von Neumann's cellular constructions, with a threshold network as the
126
- transition rule and threshold netlists as the configurations.
 
 
 
 
 
 
127
 
128
  Integer weights make the reproduction exact. A trained network that outputs an
129
  approximation of its weights loses information in every generation, while a
@@ -155,24 +160,26 @@ every string $f$ has a description of length at most
155
  $|f|+\lceil|f|/127\rceil+1$, and none shorter when $f$ has no two equal adjacent
156
  bytes (Section~\ref{sec:univ}).
157
  \item \emph{Exact self-reproduction of the host.} A program of $27$
158
- instructions, separating construction from copying as von Neumann did, whose
159
- output is the serialization of the host, its memory and its tape. This is the
160
- standard quine scheme, a program that decodes a description and then copies it,
161
- run on a universal machine. A fixed instantiation map of linear cost, outside the
162
- network, turns the output into the next running instance
163
- (Section~\ref{sec:selfrep}).
164
  \item \emph{Self-reproduction by the dynamics.} A family of ternary threshold
165
- networks $N_{G,L}$ whose state holds a stored network as records in two banks,
166
- with two tapes and three heads, each step evaluating one record; the network
167
- computes the step of this two-head machine from every state, for every $G$ and
168
- $L$ (Theorem~\ref{thm:physnet}). An organism of $133$ records builds every image
169
- of $133$ records from any description of it and hands control to it, and
170
- started on its own description, of $2930$ bits, it reproduces in every
171
- generation, the orbit of the network being periodic
172
- (Theorems~\ref{thm:construct} and~\ref{thm:organism}). These proofs are checked
173
- in Rocq. Record interpreters that address their state through an index register
174
- run one another and the constructor of~(2) as stored netlists
175
- (Section~\ref{sec:tower}).
 
 
 
176
  \item \emph{Robustness.} Every orbit survives perturbations below $1/2$ per
177
  pre-activation. The tolerances for weight and leakage errors are given by
178
  fan-in and layer width and, along an orbit, by its active inputs, and a bound
@@ -198,11 +205,12 @@ its first bytes (Section~\ref{sec:amplify}).
198
 
199
  Sections~\ref{sec:prelim} to~\ref{sec:robust} define the networks, build the
200
  host, carry out construction and self-reproduction on it and treat
201
- perturbations; Sections~\ref{sec:interp} and~\ref{sec:organism} give the
202
- two-head machine and the organism, Section~\ref{sec:tower} the record
203
- interpreters, and Section~\ref{sec:related} related work. The appendices give
204
- the measurements in full, what was executed and checked in Rocq, the proof of
205
- Theorem~\ref{thm:hosted}, and the listings.
 
206
 
207
  % =============================================================================
208
  \section{Threshold maps, transducers, and reproduction}\label{sec:prelim}
@@ -448,8 +456,9 @@ second statement follows by induction on $k$.
448
  The maps $\inst$ and $\Lev$ together with the device of
449
  Definition~\ref{def:transducer} are the \emph{instantiation map}: the network
450
  emits a description, and $\inst$, $\sigma^{-1}$ and $\Lev$ turn it into the next
451
- running network. Section~\ref{sec:organism} carries out the same step by the
452
- dynamics of a fixed network; for the host its cost is the following.
 
453
 
454
  \begin{proposition}[Cost of instantiation]\label{prop:envcost}
455
  Let $\Omega=(N,m,\tau)$ be an instance whose run halts after $T$ steps, and let
@@ -624,8 +633,11 @@ $(0,1,1,0)$.
624
 
625
  \begin{construction}[The host family]\label{con:host}
626
  For $A\ge3$ the netlist $H_A$ has the blocks of Table~\ref{tab:blocks}, in that
627
- order, each unit after its predecessors. Its state is the $A$ counter bits, a
628
- halt bit and the $A\cdot2^A$ memory bits, so $n=A+1+A2^A$. The decode lines $P_j$
 
 
 
629
  of $p$ give the three operands by reindexing, $M[p+o]_t=\bigvee_j(\mu_{j+o,t}\wedge
630
  P_j)$, and the bits of $p+3$ as disjunctions of the lines $P_j$ for which the
631
  corresponding bit of $j+3\bmod 2^A$ is $1$. The decode lines of $a$ and $b$ give
@@ -943,41 +955,44 @@ elsewhere, and let
943
  \end{construction}
944
 
945
  \begin{theorem}[Exact self-reproduction]\label{thm:selfrep}
946
- Let $\Omega^\ast=(\Nhost,m^\ast,\tau^\ast)$. Then $\Omega^\ast$ is exactly
947
- self-reproducing: it halts and $\Out(\Omega^\ast)=\ser(\Omega^\ast)$. Hence the
948
- generations
 
 
949
  $\Omega_{k+1}=\inst(\Out(\Omega_k))$ starting from $\Omega^\ast$ are constant.
950
  \end{theorem}
951
 
952
  \begin{proof}
953
  Phase~(A) consists of the instructions of $P$ with the same memory layout, and
954
  the cells $\cell{EOK}$ and $c_{\mathrm{eot}}$ are not addressed by them, so the
955
- proof of Theorem~\ref{thm:univ} applies: every token of $\tau^\ast$ is consumed
956
- in order, and when the end token is read the output word is
957
- $\delta(\tau^\ast)=\field{\sigma(\Nhost)}\cat\field{m^\ast}$, the head is at
958
- $|\tau^\ast|$, $M[\cell{Z}]=0$, $M[R_{\mathrm{out}}]=0$, $M[\cell{EOK}]=1$, and
959
- all request bits are $0$. Phase~(R) assigns $M[c_{\mathrm{rw}}]\leftarrow 1$,
960
- the result being positive, and the device sets $h=0$ and clears
961
- $c_{\mathrm{rw}}$.
962
 
963
  For phase~(B) consider the assertion
964
  \[
965
  J_i:\quad
966
  \begin{minipage}{0.78\textwidth}
967
  control reaches the head of phase~(B) with head position $i$, output word
968
- $\omega=\delta(\tau^\ast)\cat\tau^\ast_0\cdots\tau^\ast_{i-1}$,
969
  $M[\cell{Z}]=0$, $M[\cell{EOK}]=1$, $M[R_{\mathrm{out}}]=0$, all request bits $0$.
970
  \end{minipage}
971
  \]
972
  $J_0$ holds. Assume $J_i$; the first instruction requests a read. If
973
- $i<|\tau^\ast|$, the device loads $\tau^\ast_i$, clears $c_{\mathrm{eot}}$ and
974
  advances the head; the second instruction leaves $\cell{EOK}=1$, positive; the
975
- third and fourth assign $M[\cell{Z}]\leftarrow\nu(\tau^\ast_i)$ and
976
- $M[R_{\mathrm{out}}]\leftarrow\tau^\ast_i$; the fifth emits $\tau^\ast_i$; the
977
  sixth restores $M[\cell{Z}]=0$ and branches back, so $J_{i+1}$ holds. If
978
- $i=|\tau^\ast|$, the device sets $c_{\mathrm{eot}}=1$, the second instruction
979
  assigns $M[\cell{EOK}]\leftarrow0$ and transfers control to $\cell{0xFF}$, and the
980
- output word is $\delta(\tau^\ast)\cat\tau^\ast=\ser(\Omega^\ast)$. The second
 
 
981
  statement is Lemma~\ref{lem:generations}.
982
  \end{proof}
983
 
@@ -1208,7 +1223,7 @@ At $s=0.09$, $0.10$ and $0.11$, where most of the $256$ noisy copies leave the
1208
  exact orbit, the profile bound of Proposition~\ref{prop:profile} lies $2\%$ to
1209
  $19\%$ above the measured per-step rate, in the run of Table~\ref{tab:curve} and
1210
  in a repetition with a separate simulator, and $Mp$ exceeds the bound by a factor
1211
- of $2.7$. Under read noise on every
1212
  pre-activation the complete self-reproducing run emits the whole instance at
1213
  $s=0.05$ and has its halt bit set after $143$ bytes at $s=0.10$
1214
  (Table~\ref{tab:noise}). Under static weight error the orbit stays exact up to a
@@ -1216,6 +1231,55 @@ leakage of $3.9\times10^{-3}$ and a programmed-weight error of $1/16$; of the
1216
  thirteen settings of Table~\ref{tab:leak}, Corollary~\ref{cor:mismatch}
1217
  certifies three and Proposition~\ref{prop:orbit} nine.
1218
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1219
  \subsection{Fault tolerance by amplification}\label{sec:amplify}
1220
 
1221
  Theorem~\ref{thm:margin} tolerates perturbations below $\tfrac12$ because every
@@ -1535,7 +1599,8 @@ $3.11\times10^{-4}$ at $s=0.22$ and $0.271$ at $s=0.25$, where that bound exceed
1535
  one, and it exceeds one from $s=0.27$. Along the first $20{,}000$ steps of the
1536
  self-describing instance, weighted by the steps each of the $64$ noisy copies
1537
  ran, $b(t)$ lies $1.06$ to $1.47$ times above the measured failure rates
1538
- (Table~\ref{tab:bound3}), which are $21$ and $12$ times below the rates at which
 
1539
  the replicated state left its orbit at $s=0.27$ and $0.30$.
1540
 
1541
 
@@ -1546,32 +1611,71 @@ the replicated state left its orbit at $s=0.27$ and $0.30$.
1546
  This section gives a fixed network whose state holds a threshold network as
1547
  data: a program bank lists the units of the stored network as records, the step
1548
  evaluates the record in front of the bank and turns the bank by one record, and
1549
- three heads read and write a second bank and two tapes.
 
1550
 
1551
  \begin{definition}[The two-head machine]\label{def:twohead}
1552
- Let $G,L\ge1$ and $N=26G$. A state of the machine $T_{G,L}$ consists of a window
1553
- $w\in\B^{64}$, two banks $Q_0,Q_1\in\B^N$ and two tapes $D_0,D_1\in\B^L$, each
1554
- bank and tape circular and listed from its head. Window cell $0$ is the bank bit
1555
- $\kappa$; $Q_\kappa$ is the active bank and $Q_{1-\kappa}$ the idle one, $D_\kappa$
1556
- the read tape and $D_{1-\kappa}$ the copy tape. Window cells $1$, $2$ and $3$ are
1557
- not stored: they name the cell under the read head $D_\kappa[0]$, under the arm
1558
- $Q_{1-\kappa}[0]$ and under the copy head $D_{1-\kappa}[0]$, and cells $4$, $5$
1559
- and $6$ are the requests that move these three heads. A \emph{record} is a word
1560
- $(c_0,c_1,\beta,a_0,a_1,a_o)\in\B^{26}$: two weight codes of two bits, read as
 
1561
  $w=1$ for $01$, $w=-1$ for $10$ and $w=0$ otherwise, a bias $\beta\in[-8,7]$ in
1562
- two's complement on four bits, and three window addresses of six bits, most
1563
- significant first. One step reads the record $Q_\kappa[0..26)$ and the values
1564
  $x_a$ of the cells it names, and
1565
  \begin{enumerate}[label=(\roman*)]
1566
  \item computes $v=\Hv(w_0x_{a_0}+w_1x_{a_1}+\beta)$;
1567
  \item writes $v$ into window cell $a_o$ if $a_o=0$ or $a_o\ge4$, under the arm if
1568
  $a_o=2$, under the copy head if $a_o=3$, and nowhere if $a_o=1$;
1569
- \item turns $Q_\kappa$ by $26$ cells, and the read tape, the idle bank and the copy
1570
  tape by one cell each when cell $4$, $5$ or $6$ holds $1$ after the write;
1571
  \item clears cells $4$, $5$ and $6$.
1572
  \end{enumerate}
 
 
1573
  \end{definition}
1574
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1575
  A \emph{pass} is $G$ steps. The active bank comes back to the record it started
1576
  with, so a pass evaluates the $G$ records of the bank once each, in order, each
1577
  on the values the records before it left. The bank bit is read at the start of a
@@ -1579,64 +1683,355 @@ step, so a record that writes $\kappa$ passes control to the other bank from
1579
  the next step on.
1580
 
1581
  \begin{theorem}[The machine as a ternary threshold network]\label{thm:physnet}
1582
- For all $G,L\ge1$ there is a clocked ternary threshold netlist $N_{G,L}$ on the
1583
- $61+2N+2L$ bits of the stored window cells, the banks and the tapes, with
1584
- $647+8N+6L$ units, $2198+18N+12L$ edges, every weight $\pm1$ and depth $15$,
1585
- such that for every state $x$ of $T_{G,L}$ the next state $N_{G,L}$ computes
1586
- from the bits of $x$ is the encoding of the next state of $x$.
 
 
 
1587
  \end{theorem}
1588
 
1589
  \begin{proof}
1590
  The netlist has twenty-eight blocks. A multiplexer on $\kappa$ selects each bit
1591
  of the record in front of the active bank and the cell under each head. Three
1592
- blocks of $64$ units decode $a_0$, $a_1$ and $a_o$ (Lemma~\ref{lem:mem}), and a
1593
- disjunction of the conjunctions of each decode line with the cell it names reads
1594
- $x_{a_0}$ and $x_{a_1}$, cells $1$ to $3$ through the head multiplexers. From the
1595
- weight codes, units for $[w_k=1]\wedge x_{a_k}$ and $[w_k=-1]\wedge x_{a_k}$ give
1596
- $p=w_0x_{a_0}+w_1x_{a_1}\in[-2,2]$; five units test $p\ge c$ for $c=-2,\dots,2$
1597
- and five test $\beta\ge-c$ on the four bits of $\beta$, and $v$ is the disjunction
1598
- of the five conjunctions, since $p+\beta\ge0$ exactly when some $c$, namely
1599
- $c=p$, has $p\ge c$ and $\beta\ge-c$. A multiplexer on the decode line of each
1600
- stored window cell writes $v$ into it, and the three request cells read a unit
1601
- that never fires. Bit $i$ of a bank is the disjunction of three conjunctions:
1602
- with the bank active, its bit $i+26\bmod N$; with it idle, bit $i+1\bmod N$ or
1603
- bit $i$ of the bank after the arm's write, as cell $5$ after the write requests;
1604
- the cell under the arm after the write is a multiplexer of $v$ and the old cell
1605
- on the decode line of $2$. The tapes are alike, with cells $4$ and $6$ and the
1606
- decode line of $3$. Counting the units and edges of the blocks gives the sizes.
1607
- The depth of a unit depends only on its block: the record and the cells under
1608
- the heads are selected at depth $2$, the decode lines lie at depth $3$, the
1609
- operands at $5$, the products at $6$, the tests $p\ge c$ at $7$ and $v$ at $9$,
1610
- the written window cells, the requests among them, at $11$, the next bank bits
1611
- at $14$, the signals that turn the tapes at $13$ and the next tape bits at
1612
- $15$. That the next state is the encoding of the next state of $x$, for all
1613
- $G$, $L$ and $x$, is proved in Rocq, with the number of units and the weights:
1614
- every unit is given a value in terms of $x$, every unit fires to its own value
1615
- on the values of the units it reads, and induction over the units in their order
1616
- shows that the netlist computes these values, among them the next state.
 
 
1617
  \end{proof}
1618
 
1619
  \begin{remark}[Corroboration]\label{rem:physcheck}
1620
- For $G=133$ and $L=2930$, the member that Section~\ref{sec:organism} uses,
1621
- $N_{G,L}$ has $45{,}891$ units, $99{,}602$ edges and $12{,}837$ state bits, and
1622
- its depth is $15$; the file the generator writes is byte for byte the netlist
1623
- extracted from the Rocq definition. One step of $N_{5,40}$ agreed with a
1624
- transcription of Definition~\ref{def:twohead} on $300$ random full states and one
1625
- step of $N_{133,2930}$ on $20$, and three generations of the organism of
1626
- Section~\ref{sec:organism}, $2{,}762{,}676$ steps, were run on $N_{133,2930}$ in
 
 
 
 
1627
  C beside the transcription, the full state compared after every step.
1628
  \end{remark}
1629
 
1630
  % =============================================================================
1631
- \section{Self-reproduction by the dynamics}\label{sec:organism}
1632
  % =============================================================================
1633
 
1634
- Inside the two-head machine the instantiation step of
1635
- Section~\ref{sec:selfrep} is a write into the idle bank followed by a change of
1636
- $\kappa$. This section gives a stored network, the organism, that performs von
1637
- Neumann's cycle there: it translates a description into a record image in the
1638
- idle bank, copies the description onto the copy tape, and hands over control to
1639
- the image.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1640
 
1641
  \begin{definition}[The description code]\label{def:code}
1642
  A \emph{gate} is a record that computes the conjunction or the disjunction of
@@ -1658,8 +2053,8 @@ having read the whole word.
1658
  \end{definition}
1659
 
1660
  An image of $n$ records of which $e$ are not gates has a description of
1661
- $22n+8e+4$ bits, and the gates include every unit of a netlist compiled as in
1662
- Corollary~\ref{cor:compile}, conjunctions, disjunctions and negated copies.
1663
 
1664
  \begin{lemma}[Completeness of the code]\label{lem:code}
1665
  For every image $X$ of whole records, $\describe(X)$ is a description of $X$.
@@ -1675,12 +2070,13 @@ the end codon closes the induction. The proof is checked in Rocq.
1675
  \begin{construction}[The organism]\label{con:organism}
1676
  The organism $\rho$ is the image of the $133$ records listed in
1677
  Appendix~\ref{app:listings}, $3458$ bits. It uses $36$ window cells: the bank bit,
1678
- the six head and request cells, a sink, thirteen cells that hold the state of a
1679
- decoder, and fifteen temporaries, each written in a pass before it is read in
1680
- that pass. The decoder's state is a mode $m$, construct ($m=0$) or copy, a phase,
1681
- codon C, gate bits G or address bits A, a counter $z$ on five bits and a codon
1682
- register $c_3c_2c_1c_0$. One pass carries out one step of the decoder, with $r$
1683
- the bit under the read head:
 
1684
  \begin{enumerate}[label=(\roman*),leftmargin=2em]
1685
  \item in phase C, $r$ is consumed and shifted into the codon register, and in copy
1686
  mode copied under the copy head; at $z=0$ the codon decides: at end the mode
@@ -1721,7 +2117,7 @@ record. The refinement, the independence from the temporaries and the evaluation
1721
  are checked in Rocq.
1722
  \end{proof}
1723
 
1724
- \begin{theorem}[Universal construction by the dynamics]\label{thm:construct}
1725
  Let $X$ be an image of $133$ records and $d$ a description of $X$ of $L$ bits. Let
1726
  $x$ be a state of $T_{133,L}$ with $\rho$ in the active bank $Q_\kappa$ from its
1727
  first record, $d$ on the read tape from its head, and in the window clear
@@ -1752,13 +2148,13 @@ By Lemma~\ref{lem:code} every image of $133$ gates has a description of $2930$
1752
  bits, so in $T_{133,2930}$ the organism builds every image of $133$ gates, and in
1753
  $T_{133,2930+8e}$ every image of $133$ records of which $e$ are not gates.
1754
 
1755
- \begin{theorem}[Self-reproduction by the dynamics]\label{thm:organism}
1756
  Let $x_0$ be the state of $T_{133,2930}$ with $\rho$ in $Q_0$, $\describe(\rho)$
1757
  on $D_0$, $Q_1$ and $D_1$ clear, $\kappa=0$, and in the window clear requests and
1758
  the decoder in phase C with $z=3$ in construct mode. For every $g\ge1$, the state
1759
  after $g$ generations of $6924$ passes, $920{,}892$ applications of $T$ each,
1760
  holds $\rho$ in both banks and $\describe(\rho)$ on both tapes, with
1761
- $\kappa=g\bmod2$. The network $N_{133,2930}$ started from the bits of $x_0$ holds
1762
  their bits at the same steps. Its orbit is periodic from the first generation
1763
  on, with a period of two generations.
1764
  \end{theorem}
@@ -1776,281 +2172,16 @@ generations, $2{,}762{,}676$ applications of $T$, in the Rocq kernel, and are
1776
  equal, so the orbit is periodic from the first generation on.
1777
  \end{proof}
1778
 
1779
- \begin{remark}\label{rem:vonneumann}
1780
- The network $N_{133,2930}$ is the fixed physics, the organism the constructor
1781
- and the copier, and $\describe(\rho)$ the description, used twice: translated in
1782
- construct mode and copied in copy mode. The description is not the bank: a gate
1783
- is named by four bits and its eight bits are written by the organism, so the
1784
- description of $\rho$ has $2930$ bits against $3458$ for the bank. What is
1785
- reproduced is the weight set of the network that runs, since the records are
1786
- the units of the stored network, weights and biases included.
1787
- \end{remark}
1788
-
1789
- \begin{remark}[Corroboration]\label{rem:organismcheck}
1790
- Three generations from $x_0$ were run on the transcription of
1791
  Definition~\ref{def:twohead}, each of $920{,}892$ steps, and on the netlist in C
1792
  beside it (Remark~\ref{rem:physcheck}); after each both banks held $\rho$ and
1793
  both tapes $\describe(\rho)$, and the full state after the third generation
1794
- equalled the state after the first. The first generation was also evaluated in
1795
- the Rocq kernel step by step, $\kappa$ staying $0$ until its last step. The Rocq
1796
- definition of the organism and its description equal those of the
1797
- transcription, and so do the full states of $T$ after $1000$ and $20{,}000$
1798
- steps.
1799
- \end{remark}
1800
-
1801
- % =============================================================================
1802
- \section{Interpreters running interpreters}\label{sec:tower}
1803
- % =============================================================================
1804
-
1805
- The records of the two-head machine name window cells. The record interpreters
1806
- of this section address a state of $2^A$ coordinates through addresses of $A$
1807
- bits and an index register, so that a member of their family can run another
1808
- member, and the constructor of Section~\ref{sec:univ}, as stored netlists. A
1809
- member is fixed by an address width $A$, with state width $S=2^A$, a record
1810
- count $G$, a bias width of $4$ bits, a number of record banks, one or two, and
1811
- an addressed span, the number of coordinates a record may read or write. The
1812
- state $x\in\B^S$ holds, in this order from coordinate $0$: a pointer register
1813
- $\mathrm{PTR}$ of $A$ bits, most significant first; the signals
1814
- $\mathrm{OUT\_DATA}$, $\mathrm{OUT\_STROBE}$, $\mathrm{ADV}$, $\kappa$ (the bank
1815
- bit), $\mathrm{HALT}$ and a sink that no record reads; a data region; and, at the
1816
- top of the state, the record regions $Q_0$ and, for a two-bank member, $Q_1$, of
1817
- $Gb$ bits each, $b$ the record width of Definition~\ref{def:records}. Further
1818
- state bits outside $\B^S$ hold the record counter $g$, on $\lceil\log_2G\rceil$
1819
- bits, and a halt bit. The members used are those of Table~\ref{tab:cfg}; $U$
1820
- denotes the first, a map on $\B^{1029}$.
1821
-
1822
- \begin{table}[ht]
1823
- \centering\small
1824
- \begin{tabular}{@{}lrrrrrrrl@{}}
1825
- \toprule
1826
- Member & $A$ & $S$ & $G$ & $b$ & banks & span & units & Used in\\
1827
- \midrule
1828
- $U$ & $10$ & $1024$ & $11$ & $41$ & 2 & $1024$ & $13{,}034$
1829
- & Constr.~\ref{con:interp}, Rem.~\ref{rem:fanin2}\\
1830
- $U_{128}$ & $7$ & $128$ & $3$ & $32$ & 1 & $32$ & $816$
1831
- & Prop.~\ref{prop:hierarchy}, run\\
1832
- $U_{2^{17}}$ & $17$ & $131{,}072$ & $2088$ & $62$ & 1 & $1616$ & $148{,}571$
1833
- & Prop.~\ref{prop:hierarchy}, running\\
1834
- $U_{2^{15}}$ & $15$ & $32{,}768$ & $535$ & $56$ & 1 & $2808$ & $59{,}348$
1835
- & Thm.~\ref{thm:hosted}\\
1836
- \bottomrule
1837
- \end{tabular}
1838
- \caption{The members of the interpreter family used here. Where the span is
1839
- smaller than $S$ the record regions are read by index only and are not
1840
- themselves addressable.}
1841
- \label{tab:cfg}
1842
- \end{table}
1843
-
1844
- \begin{definition}[Stored netlists]\label{def:records}
1845
- A \emph{record} is a tuple
1846
- $\rho=(a_0,w_0,i_0,\,a_1,w_1,i_1,\,\beta,\,a_o,i_o)$ with addresses
1847
- $a\in\{0,\dots,S-1\}$ on $A$ bits, weights $w\in\Tern$ encoded in two bits by
1848
- $1\mapsto(0,1)$, $-1\mapsto(1,0)$ and $0\mapsto(0,0)$ or $(1,1)$, index flags
1849
- $i\in\B$, and a bias $\beta\in[-8,7]$ in two's complement on four bits, so a
1850
- record occupies $b=2(A+3)+4+(A+1)=3A+11$ bits. For a value of $\mathrm{PTR}$ the
1851
- effective address is $\eff(a,i)=(a+i\cdot\mathrm{PTR})\bmod S$, and a read
1852
- outside the span returns $0$. \emph{Evaluating} $\rho$ on a state $x$ with halt
1853
- bit $0$ produces $x'$ in two stages. First
1854
- \[
1855
- x'_{\eff(a_o,i_o)}\leftarrow
1856
- \Hv\bigl(w_0\,x_{\eff(a_0,i_0)}+w_1\,x_{\eff(a_1,i_1)}+\beta\bigr),
1857
- \]
1858
- all other coordinates being unchanged. Then the \emph{pointer clause}: if
1859
- $x'_{\mathrm{ADV}}=1$, $\mathrm{PTR}$ is incremented modulo $S$ and
1860
- $\mathrm{ADV}$ is cleared. The next halt bit is the disjunction of the halt bit
1861
- with $x_{\mathrm{HALT}}$, and a state with halt bit $1$ is left unchanged. A
1862
- \emph{stored netlist} of length $G$ is a sequence $\rho_0,\dots,\rho_{G-1}$
1863
- encoded in the active record region, $Q_\kappa$. The interpreter applied to a
1864
- state with halt bit $0$ and record counter $g$ evaluates $\rho_g$ and sets the
1865
- counter to $g+1\bmod G$. The output clause is carried out by the runtime: when
1866
- $x'_{\mathrm{OUT\_STROBE}}=1$ the runtime appends $x'_{\mathrm{OUT\_DATA}}$ to an
1867
- output word and clears the strobe.
1868
- \end{definition}
1869
-
1870
- \begin{construction}[The interpreter netlist]\label{con:interp}
1871
- The clocked ternary threshold netlist $N_U$ on $1029$ state bits has $13{,}034$
1872
- units, and $\Lev(N_U)$ has depth $53$; the same blocks give every member of the
1873
- family. A one-hot decoding of the counter by units of the form of
1874
- Lemma~\ref{lem:mem} and, for each record position, a multiplexer on $\kappa$ and
1875
- a disjunction over the records select the bits of $\rho_g$. For each slot the
1876
- conjunctions of the pointer bits with the index flag and an $A$-bit ripple-carry
1877
- chain of full adders form the effective address, which the
1878
- units of Lemma~\ref{lem:mem} decode, and a disjunction of conjunctions reads the
1879
- addressed coordinate. The units $\mathrm{AND}(\overline h,l)$ and
1880
- $\mathrm{AND}(h,\overline l)$ decode a weight code $(h,l)$, and with the read
1881
- value $v$ give $w\cdot v$ in two's complement on six bits, which hold
1882
- $|w_0v_0+w_1v_1+\beta|\le10$; two ripple-carry chains form that sum, and the
1883
- negation of its sign bit is $\Hv$ of the sum. The decoded output address drives a
1884
- multiplexer for each coordinate of the span, which writes the value when the
1885
- halt bit is $0$. The pointer clause adds
1886
- $\mathrm{AND}(\hat x_{\mathrm{ADV}},\overline{\mathrm{halt}})$ to the written
1887
- pointer, the carry into bit $j$ one unit of fan-in $j+1$, and clears
1888
- $\mathrm{ADV}$; the counter is incremented by a ripple-carry chain, reset on the
1889
- one-hot line of $G-1$ and held when the halt bit is $1$, and the next halt bit is
1890
- the disjunction of the halt bit with $x_{\mathrm{HALT}}$. These blocks carry out
1891
- the interpreter of Definition~\ref{def:records} on states whose halt bit is $0$
1892
- and hold a state with halt bit $1$ fixed.
1893
- \end{construction}
1894
-
1895
- \begin{remark}[Corroboration]\label{rem:interpcheck}
1896
- The netlist of $U$ was compared with a transcription of
1897
- Definition~\ref{def:records} on all $576$ configurations of
1898
- $(w_0,w_1,\beta,x_{a_0},x_{a_1})$ and on $4096$ uniformly random full states,
1899
- stored records, pointer, $\mathrm{ADV}$ and counter included; halted states were
1900
- checked to be fixed points and the counter to advance modulo $G$ from every
1901
- value. The netlists of $U_{2^{17}}$ and $U_{2^{15}}$ ran the passes of
1902
- Remarks~\ref{rem:hierarchycheck} and~\ref{rem:hostedcheck}.
1903
- \end{remark}
1904
-
1905
- \begin{definition}[Pass]\label{def:pass}
1906
- A \emph{pass} consists of $G$ consecutive applications of the interpreter
1907
- starting with record counter $0$.
1908
- \end{definition}
1909
-
1910
- \begin{lemma}[Netlists with feedback]\label{lem:feedback}
1911
- Let $\rho_0,\dots,\rho_{G-1}$ be any stored netlist with $\mathrm{PTR}$ constant and
1912
- let $\theta_g\colon\B^S\to\B^S$ evaluate $\rho_g$. Then one pass computes
1913
- $\theta_{G-1}\circ\dots\circ\theta_0$, the sequential update of the automata
1914
- network in record order, and $k$ passes compute its $k$-th iterate.
1915
- \end{lemma}
1916
-
1917
- \begin{proof}
1918
- Definitions~\ref{def:records} and~\ref{def:pass}.
1919
- \end{proof}
1920
-
1921
- Gate-level logic simulation engines evaluate one stored gate per step from
1922
- operand memory and rank-order the gates so that one pass
1923
- suffices~\cite{Denneau1982}, and Lemma~\ref{lem:feedback} is the fixed-order
1924
- sequential schedule of automata networks~\cite{RiosWilsonTheyssier2024c}.
1925
-
1926
- \begin{corollary}[Compilation]\label{cor:compile}
1927
- Every ternary threshold unit $\Hv(\sum_{i\in P}x_i-\sum_{i\in N}x_i+b)$ is a
1928
- threshold over its literals: it is $1$ exactly when at least $t=|N|-b$ of the
1929
- literals $x_i$ ($i\in P$), $\neg x_i$ ($i\in N$) are true. A disjunction
1930
- ($t=1$) or a conjunction ($t=k$) of $k>2$ literals is computed by a balanced
1931
- binary tree of $k-1$ two-input units, negated literals being carried by the signs
1932
- of the weights, and a threshold $1<t<k$ by the recursion
1933
- $T_t(x_1,\dots,x_k)=T_t(x_2,\dots,x_k)\vee(x_1\wedge T_{t-1}(x_2,\dots,x_k))$.
1934
- An acyclic netlist of fan-in at most $2$ whose signals and records fit in the
1935
- data region and the record region, compiled one record per unit in topological
1936
- order, is evaluated by one pass.
1937
- \end{corollary}
1938
-
1939
- \begin{proof}
1940
- For the trees pair the literals at the leaves: $\Hv(\pm x\pm y+c)$ with
1941
- $c\in\{0,-1,-2\}$ is the conjunction of any two literals and with
1942
- $c\in\{-1,0,1\}$ their disjunction; each level of the tree halves the number of
1943
- signals, and a signal left over at a level is carried to the next. The recursion
1944
- holds because at least $t$ of $x_1,\dots,x_k$ are true exactly when at least $t$
1945
- of $x_2,\dots,x_k$ are, or $x_1$ is and at least $t-1$ of the others are; its
1946
- two base cases are the trees. A record carries the addresses of the unit's
1947
- predecessors with index flag $0$, its weights and its bias, and writes a fresh
1948
- data address, so by Lemma~\ref{lem:feedback} each record reads values already
1949
- computed in the pass.
1950
- \end{proof}
1951
-
1952
- \begin{remark}\label{rem:fanin2}
1953
- Every unit of fan-in greater than two in $N_U$ is a disjunction or a conjunction
1954
- of literals, and in $\Nhost$ so is every one except the fifteen units of the sum
1955
- bits and the branch, for which the recursion of Corollary~\ref{cor:compile}
1956
- gives four and eight units; the $17{,}598$ units of $\Nhost$ become $33{,}568$
1957
- and the $13{,}034$ of $N_U$ become $40{,}081$, and the rewriting agrees with the
1958
- original netlists on random states. Records may read and write record fields,
1959
- their own included, inside the span: stored netlists of $U$ were run that emit
1960
- their own records, rewrite a weight of their own record between passes, switch
1961
- the active bank, and hold a two-bit counter through feedback.
1962
- \end{remark}
1963
-
1964
- \begin{proposition}[A larger member runs a smaller one]\label{prop:hierarchy}
1965
- For an address width $A$ let $N_2$ be the netlist of fan-in at most $2$ obtained
1966
- from the netlist of $U_{2^A}$ by Corollary~\ref{cor:compile}, with $n_2(A)$
1967
- units, let $c(A)$ be the number of state coordinates of $U_{2^A}$ whose next value
1968
- is not the coordinate itself, and let $m(A)$ be the number of state bits of
1969
- $U_{2^A}$ plus $n_2(A)$. If a one-bank member $U_{S'}$, $S'=2^{A'}$, has
1970
- $G'\ge n_2(A)+c(A)$ records and addressed span at least $m(A)$, then the stored
1971
- netlist consisting of the records of $N_2$ in topological order followed by one
1972
- copy record for each of the $c(A)$ coordinates satisfies: for every state $y$ of
1973
- $U_{2^A}$ held in the input coordinates, one pass of $U_{S'}$ replaces $y$ by
1974
- $U_{2^A}(y)$ there. So $k$ passes carry out $k$ transitions, and $U_{S'}$ runs
1975
- $U_{2^A}$, stored netlist and all. Such $A'$ exist for every $A$, and
1976
- $S'=O(2^AA^2)$ suffices.
1977
- \end{proposition}
1978
-
1979
- \begin{proof}
1980
- By Lemma~\ref{lem:feedback} a pass evaluates the records in order. The records of
1981
- $N_2$ come in topological order and write fresh coordinates, so each reads its
1982
- predecessors after they have been evaluated on $y$, the input coordinates being
1983
- unmodified until the copy records; after them every unit signal holds its value on
1984
- $y$. Each copy record reads a unit signal, which no copy record writes, and writes
1985
- a distinct input coordinate, so after all of them the input coordinates hold
1986
- $U_{2^A}(y)$, those not copied keeping their values, which are their next values.
1987
- No next value of $U_{2^A}$ is another input coordinate, since every next value is
1988
- a multiplexer output, a disjunction, or the coordinate itself. A record of
1989
- $U_{S'}$ occupies $3A'+11$ bits, so the conditions say that the records fit in
1990
- $G'(3A'+11)$ bits and the signals in $S'-G'(3A'+11)$. Since $U_{2^A}$ has
1991
- $O(2^A)$ units of total fan-in $O(2^AA)$, $n_2(A)=O(2^AA)$, and
1992
- $2^{A'}\ge c\,2^AA\,A'$ holds for $A'=A+O(\log A)$.
1993
- \end{proof}
1994
-
1995
- \begin{remark}\label{rem:hierarchycheck}
1996
- For $A=7$ the netlist of $U_{128}$ has $816$ units and its fan-in-two form $1403$
1997
- units on $1534$ signals, and $35$ of its $131$ state coordinates change in a
1998
- transition. The member with $A'=17$ and $G'=2088$ records of $62$ bits has
1999
- $148{,}571$ units and span $1616$. From $48$ uniformly random states of $U_{128}$,
2000
- stored records included, four passes of $2088$ applications each reproduced four
2001
- transitions of $U_{128}$ in every one of its $131$ coordinates.
2002
- \end{remark}
2003
-
2004
- The host's constructor runs inside the family as a stored netlist. Let $P^{128}$
2005
- be the program $P$ with its variable and device cells placed at the same offsets
2006
- from the top of a $128$-byte memory, so that $\cell{0xF0}$ to $\cell{0xFF}$
2007
- become $\cell{0x70}$ to $\cell{0x7F}$; $P$ occupies $\cell{0x00}$ to $\cell{0x3E}$
2008
- and its branch targets are unchanged. The hosted transducer is that of
2009
- Definitions~\ref{def:transducer} and~\ref{def:subleq} with addresses modulo
2010
- $128$, halting at $\cell{0x7F}$, whose tape is the content of a $64$-byte tape
2011
- region and whose output word is held in a $64$-byte output region.
2012
-
2013
- \begin{theorem}[The constructor as a stored netlist]\label{thm:hosted}
2014
- In the member $U_{2^{15}}$ of Table~\ref{tab:cfg} there is a stored netlist of
2015
- $494$ records, holding in data coordinates a $128$-byte memory as eight bit
2016
- planes, the tape region, the output region, the program counter, the head and
2017
- the write pointer, such that one pass carries out one step of the hosted
2018
- transducer from every state in which the head is at most $64$, the write pointer
2019
- at most $63$, the output region clear from the write pointer on and the
2020
- pointer-advance, strobe and halt signals clear; at a step that reaches the halt
2021
- address the interpreter halts within the pass, with the memory, the head and
2022
- the output as the step leaves them. Consequently, for every recipe $r$ with
2023
- $|r|\le64$ and $|\delta(r)|\le63$, the interpreter started from the state holding
2024
- $P^{128}$ and $r$ halts after exactly as many passes as Theorem~\ref{thm:univ}
2025
- gives steps, with the output region holding $\delta(r)$.
2026
- \end{theorem}
2027
-
2028
- \begin{proof}
2029
- A pass evaluates the records in order (Lemma~\ref{lem:feedback}). The records
2030
- form cells, each a fixed sequence of records computing a Boolean function of the
2031
- current values of its sources into a destination through temporaries written
2032
- once: copies, negations, conjunctions and disjunctions of two literals, exclusive
2033
- ors, multiplexers, conjunctions of literals as balanced trees, the addition of a
2034
- constant and an increment gated by a bit; an indexed read or write is a single
2035
- record. In order, the pass forms $p+1$, $p+2$, $p+3$; fetches the three operands
2036
- and the two words they address through the index register; subtracts and tests
2037
- the result; writes it back; applies the device to the memory so updated; and
2038
- replaces the counter by the branch target, raising the halt signal at the halt
2039
- address. Appendix~\ref{app:hosted} proves each cell and each stage, the
2040
- invariants of the run, and the halt.
2041
- \end{proof}
2042
-
2043
- \begin{remark}\label{rem:hostedcheck}
2044
- $U_{2^{15}}$ has $59{,}348$ units in $61$ levels. On the recipe for
2045
- \texttt{ABCCCCCCCCDE}, which exercises a literal token, a repeat token and the end
2046
- token, the interpreter halted in pass $83$, as Theorem~\ref{thm:univ} predicts,
2047
- at application $44{,}358$, with the output region holding the target byte
2048
- for byte, the head at the end of the recipe, and all $128$ memory bytes equal to
2049
- those of the reference machine. One pass was compared with one step of the hosted
2050
- transducer from $2000$ random states on a transcription of
2051
- Definition~\ref{def:records} and from $256$ random states on the netlist, the
2052
- memory, counter, tape, head, write pointer and output below it random, and agreed
2053
- in every case.
2054
  \end{remark}
2055
 
2056
  % =============================================================================
@@ -2094,7 +2225,8 @@ looped transformer whose state tensor holds the program~\cite{Turkcan2026}. Thes
2094
  constructions use real-valued weights, and Loom includes a layer that restores
2095
  persistent state to $\pm1$ after numerical drift. The two-head machine of
2096
  Section~\ref{sec:interp} is a fixed ternary network of the same kind, which
2097
- evaluates any network stored in its own state.
 
2098
  Valiant's universal circuits are combinational circuits of size
2099
  $\Theta(n\log n)$ that simulate any circuit of size $n$ given its program bits as
2100
  input~\cite{Valiant1976}; intrinsic simulation between families of automata
@@ -2166,9 +2298,11 @@ not imply it, and require that the maps mediating between symbolic computation a
2166
  the dynamics be simpler than the computation attributed to the
2167
  system~\cite{CotlerHonglerHudcova2025}. In those terms Section~\ref{sec:selfrep}
2168
  is a replicator relative to an instantiation map whose cost
2169
- Proposition~\ref{prop:envcost} counts, and Section~\ref{sec:organism} is von
2170
- Neumann's constructor in the configuration space of a fixed threshold network,
2171
- with the configuration holding the weights of the machine that runs.
 
 
2172
 
2173
  \subsection{Networks that produce their own parameters}
2174
 
@@ -2377,35 +2511,39 @@ or from its $75{,}501$ nonzero entries; and (d) an evaluator in C,
2377
  \texttt{src/netlist\_eval.c}, with its own SUBLEQ reference, device, parser and
2378
  levelization, sharing no code with (a)--(c). Evaluators (b)--(d) are built from
2379
  $\sigma(\Nhost)$, and every generation of every run instantiated its evaluator
2380
- afresh from the bytes the previous generation emitted. The record interpreters
2381
- were evaluated by a transcription of Definition~\ref{def:records}, by the netlist
2382
- of each member, by $\Lev(N_U)$ and by evaluator~(d), and the two-head machine by a
2383
- transcription of Definition~\ref{def:twohead}, by the netlist $N_{G,L}$ in Python
2384
- and in C, and in the Rocq kernel. Separate code in \texttt{src/independent/},
2385
- written from the definitions of this paper, rebuilds $H_A$ from
2386
- Construction~\ref{con:host} for $A=3$ to $12$, levelizes $\sigma(\Nhost)$,
2387
- transcribes Definition~\ref{def:records} to run Proposition~\ref{prop:hierarchy}
2388
- and Theorem~\ref{thm:hosted}, repeats Tables~\ref{tab:curve} to~\ref{tab:bound3}
 
 
2389
  with a simulator in C and on the accelerator, and maps the datapath onto Loihi
2390
  and Akida; its sizes, depths and exact runs are those stated here, and its noisy
2391
  runs agree with Appendix~\ref{app:measure} within sampling error.
2392
 
2393
  The Rocq development, \texttt{rocq/} in the artifact, has $25$ files and
2394
- $3{,}802$ lines and builds with Rocq 9.1.1 in under four minutes on the machine
2395
- below, most of it in the kernel's evaluations of the first three generations. It
2396
- defines the two-head machine, its netlist block by block, the organism, the
2397
- description code and the decoder, and proves Theorem~\ref{thm:physnet} for every
2398
- bank length $N\ge26$ and tape length $L\ge1$ together with the number of units
2399
- and the weights, Lemma~\ref{lem:code}, Proposition~\ref{prop:pass},
2400
- Theorem~\ref{thm:construct} for images of any number of records with an idle
2401
- bank of their length, and Theorem~\ref{thm:organism}. The file that defines the
 
 
2402
  organism and its description is written by \texttt{src/twohead.py}, and the
2403
- netlist definition, extracted to OCaml, writes a file byte for byte equal to the
2404
- netlist file \texttt{variants/twohead\_physics.bin} that
2405
  \texttt{src/twohead\_net.py} writes.
2406
 
2407
  Besides the runs recorded in the remarks of Sections~\ref{sec:host}
2408
- to~\ref{sec:tower} and in Appendix~\ref{app:measure}, the following were
2409
  checked. The encoder satisfies $\delta(\describe(f))=f$ within the bound of
2410
  Lemma~\ref{lem:describe} on the $29$ serialized machines of the artifact,
2411
  $971{,}266{,}155$ bytes, and on $68{,}061$ further strings chosen to straddle
@@ -2419,9 +2557,8 @@ $16{,}320$ steps and the device with its four clauses on $20{,}580$ applications
2419
  $\inst(\ser(\Omega))=\Omega$ on $310$ instances; and rebuilding the host from
2420
  source reproduces its serialization byte for byte. Evaluator~(d) agrees with its
2421
  own reference on $2^{20}$ random full states of the netlist and of its
2422
- levelization, and $\Lev(\Nhost)$ and $\Lev(N_U)$ agree with the netlists they come
2423
- from on random states, every weight in $\Tern$ and every pre-activation an
2424
- integer.
2425
 
2426
  The constructor was run on the $29$ recipes of the artifact's machines on the
2427
  integer reference, emitting $971{,}266{,}155$ bytes in $4{,}714{,}592{,}952$ steps,
@@ -2458,179 +2595,57 @@ generations on~(d) ran in lockstep.}
2458
  Every run of $\Omega^\ast$ produced the same output, of SHA-256 digest
2459
  \texttt{c5950d3f\ldots6e7fb523}, and the artifact~\cite{Norton2026} is identified by
2460
  the digests of $\ser(\Omega^\ast)$, of the host serialization, of the serialization
2461
- of $N_3$, of $\ser(\Omega^\ast_3)$, of the netlist file of $N_{133,2930}$, and of
2462
  the organism's record image and its description written as $3458$ and $2930$
2463
- bytes of value $0$ or $1$:
 
2464
  \begin{center}\ttfamily\small
2465
  \begin{tabular}{@{}l@{\quad}l@{}}
2466
  \normalfont $\ser(\Omega^\ast)$ & c5950d3fec6bcf70e8111586a030d21b2dfa55bd69a1c7fe5f016cb06e7fb523\\
2467
  \normalfont $\sigma(\Nhost)$ & 55c555bd64302190d5bde57ee0cd41b36dac5cf65066d0ed3468567a57e85fa3\\
2468
  \normalfont $\sigma(N_3)$ & 111bb202e6f2728449c6b0761f57a059bc0fb38e3e5ba8447bac6613ec7ce313\\
2469
  \normalfont $\ser(\Omega^\ast_3)$ & 05005d59c373fb4998576ac098093de6df046355348e00370dbdccb1d458e3de\\
2470
- \normalfont $N_{133,2930}$ & 0b86fbe2da1d67beafb252a445738794f4eb35ad4aa8987030c65d3612aa50fe\\
2471
  \normalfont organism $\rho$ & e798ce81da252d6717b0f99321aa401924637b48fe09558a6590006de2e2ffe1\\
2472
- \normalfont $\describe(\rho)$ & 59c917d4b2602b253bbae19d9866869587abea197730197fee8054acc8781d54
 
 
2473
  \end{tabular}
2474
  \end{center}
2475
- The script \texttt{src/verify.py} runs the checks in three tiers, the checks of a
2476
- tier concurrently: the statements, sixteen checks that execute no construction
2477
- and take $3.6$ minutes of wall time on the machine below; every construction end
2478
- to end; and the runs on the threshold evaluators with the perturbation
2479
- experiments. One generation of $\Omega^\ast$ takes $0.3$ seconds on~(a), $45$
2480
- seconds on~(d) and $9$ minutes on~(b), and the artifact records the rate of every
2481
- evaluator. All figures were
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2482
  produced with Python 3.12.3, PyTorch 2.14.0 with CUDA 12.6, NumPy 2.5.3,
2483
  safetensors 0.8.0 and GCC 13.3.0 on Ubuntu 24.04, on an Intel Core i9-12900H
2484
  with $20$ hardware threads and $31$ GiB of memory and an NVIDIA GeForce RTX 3070
2485
  Ti Laptop GPU with $8$ GiB of memory.
2486
 
2487
- % =============================================================================
2488
- \section{Proof of Theorem~\ref{thm:hosted}}\label{app:hosted}
2489
- % =============================================================================
2490
-
2491
- Throughout, $U'=U_{2^{15}}$: $A=15$, one bank of $G=535$ records of $b=56$ bits
2492
- at the coordinates from $2808$ on, and the addressed span the coordinates $0$ to
2493
- $2807$. Coordinates $0$ to $14$ hold $\mathrm{PTR}$, most significant first,
2494
- $15$ to $19$ hold $\mathrm{OUT\_DATA}$, $\mathrm{OUT\_STROBE}$, $\mathrm{ADV}$,
2495
- $\kappa$ and $\mathrm{HALT}$, and $20$ is the sink. The stored netlist is the
2496
- $494$ records described below, followed by $41$ records with both weights $0$
2497
- and bias $-1$ that write $0$ into the sink, which no record reads. Its signals
2498
- are the work region, coordinates $21$ to $720$, holding the registers
2499
- $\mathrm{PC}$, $P_1$, $P_2$, $P_3$, $\mathrm{NPC}$, $\mathrm{head}$ and $\mathrm{wp}$
2500
- of seven bits and $A$, $B$, $C$, $X$, $Y$, $R$ of eight bits, least significant
2501
- bit first, and the temporaries; the memory, bit $t$ of byte $j$ at $721+128t+j$;
2502
- the tape region, bit $t$ of byte $i$ at $1745+64t+i$; and the output region, bit
2503
- $t$ of byte $i$ at $2257+64t+i$, bit $0$ being the least significant.
2504
-
2505
- A hosted state $(M,p,\tau,h,\omega)$, with $M\in\Sigma^{128}$, $p<128$,
2506
- $\tau\in\Sigma^{64}$, $h\le64$ and an output word $\omega$ of $w\le63$ bytes, is
2507
- \emph{held} by a state $x$ of $U'$ when the memory, the tape region and the
2508
- first $w$ bytes of the output region hold $M$, $\tau$ and $\omega$, the bytes
2509
- from $w$ on of the output region are $0$, the registers $\mathrm{PC}$,
2510
- $\mathrm{head}$ and $\mathrm{wp}$ hold $p$, $h$ and $w$, the signals
2511
- $\mathrm{ADV}$, $\mathrm{OUT\_STROBE}$ and $\mathrm{HALT}$ are $0$, and the record
2512
- counter and the halt bit are $0$.
2513
-
2514
- \subsection*{Records}
2515
- A pass evaluates the records in order, each on the state the records before it
2516
- left (Lemma~\ref{lem:feedback}). The first eight records clear the bits $7$ to
2517
- $14$ of $\mathrm{PTR}$, and the pass writes the low seven bits only by copying a
2518
- register into them, so every indexed slot names a coordinate $\mathrm{base}+p'$
2519
- with $p'\le127$; the bases and offsets used below keep every named coordinate in
2520
- the work, memory, tape or output region or at most one coordinate past the tape
2521
- region, all inside the span, so no read returns the default $0$ in place of a
2522
- stored value. No record writes $\mathrm{ADV}$, $\mathrm{OUT\_STROBE}$, $\kappa$ or
2523
- the bank, so the pointer and output clauses never fire and the stored netlist
2524
- does not change.
2525
-
2526
- \subsection*{Cells}
2527
- Each temporary is the destination of one record, which precedes every record
2528
- that reads it. A record with weights $w_0,w_1$ and bias $\beta$ writes
2529
- $\Hv(w_0x_0+w_1x_1+\beta)$, so the following single records are correct on
2530
- $\B$: a constant, bias $0$ or $-1$ and no input; a copy $\Hv(x-1)$; a negation
2531
- $\Hv(-x)$; a conjunction of two literals, weights $\pm1$ by polarity and bias
2532
- minus the number of positive literals; a disjunction $\Hv(x+y-1)$; an indexed
2533
- read $y\leftarrow x_{\mathrm{base}+\mathrm{PTR}}$, a copy whose input slot carries
2534
- the index flag; an indexed write $x_{\mathrm{base}+\mathrm{PTR}}\leftarrow y$, a
2535
- copy whose output carries the index flag; and the indexed write of $y\wedge g$, a
2536
- conjunction whose output carries the flag. The cells of several records are the
2537
- following, each changing its destination and its temporaries and nothing else.
2538
- \begin{enumerate}[label=(\roman*),leftmargin=2em]
2539
- \item The exclusive or of $a$ and $b$ writes $t_1=a\vee b$, then
2540
- $t_2=\Hv(1-a-b)=\neg(a\wedge b)$, then $t_1\wedge t_2=a\oplus b$. Its last record
2541
- reads only $t_1$ and $t_2$, so the cell is correct when its destination is $a$ or
2542
- $b$.
2543
- \item The multiplexer $d\leftarrow s\,?\,x_1:x_0$ writes $\neg s$, $x_1\wedge s$,
2544
- $x_0\wedge\neg s$ and their disjunction; its last record reads only the two
2545
- conjunctions, so it is correct when $d$ is $x_0$.
2546
- \item The conjunction of $k$ literals pairs them at the leaves of a balanced tree,
2547
- a single literal left over being copied or negated, and conjoins the results
2548
- level by level; the root writes the destination.
2549
- \item The addition of a constant $c$ to a register $s$ on seven bits treats the bit
2550
- positions from the least significant up, with no carry before the first position
2551
- at which $c$ has a $1$: such a position $k$ writes $d_k=\neg s_k$ and starts the
2552
- carry $s_k$; a later position with a $1$ writes $d_k=\neg(s_k\oplus\gamma)$ and the
2553
- carry $s_k\vee\gamma$; a position with a $0$ writes $d_k=s_k\oplus\gamma$ and the carry
2554
- $s_k\wedge\gamma$, or copies $s_k$ while there is no carry. These are the sum and the
2555
- carry of $s_k+c_k+\gamma$, so $d=(s+c)\bmod128$.
2556
- \item The increment of a seven-bit register $s$ by a bit $g$ first forms the carries
2557
- $\gamma_0=g$ and $\gamma_{k+1}=s_k\wedge\gamma_k$ for $k<6$, and then replaces each
2558
- $s_k$ by $s_k\oplus\gamma_k$, by~(i); the carries are formed before any bit
2559
- changes, so $s$ becomes $(s+g)\bmod128$.
2560
- \end{enumerate}
2561
-
2562
- \subsection*{The pass}
2563
- Let $x$ hold $(M,p,\tau,h,\omega)$ with $p\ne\cell{0x7F}$ as the start of a pass,
2564
- let $(M',p',h',\omega')$ be the result of one step of the hosted transducer, and
2565
- write $\pi(v)$ for the copies of the seven low bits of a register $v$ into the
2566
- low bits of $\mathrm{PTR}$, which set $\mathrm{PTR}$ to $v\bmod128$.
2567
- \begin{enumerate}[label=(\alph*),leftmargin=2em]
2568
- \item Eight constants clear the high bits of $\mathrm{PTR}$, and three additions of
2569
- a constant set $P_1$, $P_2$, $P_3$ to $p+1$, $p+2$ and $p+3$ modulo $128$.
2570
- \item $\pi(\mathrm{PC})$ and eight indexed reads at the bases of the eight memory
2571
- planes set $A=M[p]$, bit $t$ from plane $t$; likewise $\pi(P_1)$ and $\pi(P_2)$
2572
- set $B=M[p+1]$ and $C=M[p+2]$, and $\pi(A)$ and $\pi(B)$ set $X=M[a]$ and $Y=M[b]$,
2573
- where $a$ and $b$ are $A$ and $B$ modulo $128$.
2574
- \item Eight negations form the bits of $\neg X$. Position $0$ forms
2575
- $u=y_0\oplus\neg x_0$, $R_0=\neg u$ and the carry $y_0\vee\neg x_0$, the sum and carry
2576
- of $y_0+\neg x_0+1$; each position $k\ge1$ forms $u=y_k\oplus\neg x_k$,
2577
- $R_k=u\oplus\gamma$ and the carry $(y_k\wedge\neg x_k)\vee(u\wedge\gamma)$, the sum and
2578
- carry of $y_k+\neg x_k+\gamma$. Hence $R=Y+\bar X+1=(Y-X)\bmod256$. A chain of
2579
- disjunctions and a negation form $[R=0]$, and its disjunction with $R_7$ is
2580
- $[R\le0]$ in two's complement, as in Lemma~\ref{lem:branch}.
2581
- \item $\pi(B)$ and eight indexed writes store $R$ at $M[b]$. The memory is now the
2582
- memory after the instruction of Definition~\ref{def:subleq}, which we call $N$.
2583
- \item The device acts on $N$. Three conjunctions of eight literals, bit $0$
2584
- positive and bits $1$ to $7$ negated, form the requests
2585
- $\mathrm{wr}=[N[c_{\mathrm{wr}}]=1]$, $\mathrm{rd}$ and $\mathrm{rw}$; a conjunction of
2586
- the literals of $\mathrm{head}$ against the bits of $64$ forms $[h=64]$, which for
2587
- $h\le64$ is $[h\ge64]$, and $g=\mathrm{rd}\wedge\neg[h=64]$. Then $\pi(\mathrm{head})$
2588
- and eight indexed reads of the tape planes give $\tau_h$ when $h<64$; eight
2589
- multiplexers on $g$ load it into $R_{\mathrm{in}}$; a multiplexer on $\mathrm{rd}$
2590
- sets bit $0$ of $c_{\mathrm{eot}}$ to $[h=64]$ and seven conjunctions clear its
2591
- other bits when $\mathrm{rd}=1$; an increment adds $g$ to $\mathrm{head}$, which
2592
- stays at most $64$, and seven conjunctions with $\neg\mathrm{rw}$ clear it on a
2593
- rewind. Then $\pi(\mathrm{wp})$ and eight indexed writes store
2594
- $R_{\mathrm{out}}\wedge\mathrm{wr}$ at byte $w$ of the output region, eight
2595
- conjunctions with $\neg\mathrm{wr}$ clear $R_{\mathrm{out}}$ when it was emitted, an
2596
- increment adds $\mathrm{wr}$ to $\mathrm{wp}$, and twenty-four constants clear the
2597
- three request cells. These are the clauses of Definition~\ref{def:transducer} on
2598
- $N$: a read at $h<64$ loads $\tau_h$, clears $c_{\mathrm{eot}}$ and advances the
2599
- head, a read at $h=64$ sets $c_{\mathrm{eot}}$ and leaves $R_{\mathrm{in}}$ and the
2600
- head, the rewind follows the read, and a write appends $R_{\mathrm{out}}$ to the
2601
- output word and clears it. The clauses touch disjoint cells apart from the head,
2602
- which the read changes before the rewind, as the definition orders them. When
2603
- $\mathrm{wr}=0$ the indexed writes store $0$ at byte $w$, which was $0$. So the
2604
- memory, the head, the output region and $\mathrm{wp}$ now hold $M'$, $h'$,
2605
- $\omega'$ and the length of $\omega'$.
2606
- \item Seven multiplexers on $[R\le0]$ set $\mathrm{NPC}$ to $C\bmod128$ or to $P_3$,
2607
- which is the counter $p'$ of Definition~\ref{def:subleq} modulo $128$; a
2608
- conjunction of the literals of $\mathrm{NPC}$ against the bits of $\cell{0x7F}$
2609
- writes $[p'=\cell{0x7F}]$ into $\mathrm{HALT}$; and seven copies set $\mathrm{PC}$ to
2610
- $\mathrm{NPC}$.
2611
- \end{enumerate}
2612
- If $p'\ne\cell{0x7F}$ the state after the pass holds $(M',p',\tau,h',\omega')$,
2613
- with $\mathrm{HALT}=0$ and the record counter back at $0$ after the no-op records.
2614
- If $p'=\cell{0x7F}$, the record that writes $\mathrm{HALT}$ is followed by one more
2615
- application, which evaluates the first copy into $\mathrm{PC}$ and sets the halt
2616
- bit, the next halt bit being the disjunction with $x_{\mathrm{HALT}}$; after it the
2617
- state is fixed, with the memory, the head and the output as the step leaves
2618
- them. This proves the first statement of the theorem.
2619
-
2620
- \subsection*{The run}
2621
- Let $r$ be a recipe with $|r|\le64$ and $|\delta(r)|\le63$, and let $\tau$ be $r$
2622
- followed by $64-|r|$ bytes $0$. The program $P^{128}$ is $P$ with the cells it
2623
- names moved to the same offsets from the top of the memory, and the proof of
2624
- Theorem~\ref{thm:univ} uses only the transducer, so it holds for $P^{128}$ on
2625
- $\tau$: the run halts after the number of steps the theorem gives, with output
2626
- $\delta(r)$, and it never reads past the end token, so the bytes after $r$ are
2627
- never read and the head stays at most $|r|\le64$. Its output word is a prefix of
2628
- $\delta(r)$ at every step, so it has at most $63$ bytes. The state holding $P^{128}$
2629
- and $r$ holds the initial hosted state, so by induction over the steps, the
2630
- hypothesis of the first statement holding at each, every pass but the last
2631
- carries out a step and the last carries out the halting step and halts, with the
2632
- output region holding $\delta(r)$.
2633
-
2634
  % =============================================================================
2635
  \section{Program listings and the organism}\label{app:listings}
2636
  % =============================================================================
@@ -2702,12 +2717,14 @@ $63$ bytes of $P$.
2702
  consists of the $133$ records below, in the order a pass evaluates them; record
2703
  $k$ is written $k{:}\;{\pm}a\,{\pm}b\;\beta{\to}o$, reading window cells $a$ and $b$
2704
  with weights $\pm1$ and writing cell $o$, and a record with one slot has weight
2705
- $0$ in the other. Cell $\kappa$ is $0$, $\mathrm{rd}$, $\mathrm{arm}$ and
2706
- $\mathrm{cpy}$ are the cells under the read head, the arm and the copy head,
2707
- $\mathrm{AR}$, $\mathrm{AA}$ and $\mathrm{AC}$ the requests that turn them, $m$
2708
- the mode, $\mathrm{C}$, $\mathrm{G}$ and $\mathrm{A}$ the phases, $c_3c_2c_1c_0$
2709
- the codon register and $z_4\cdots z_0$ the counter, in cells $8$ to $20$, and
2710
- $t_{21}$ to $t_{35}$ the temporaries.
 
 
2711
 
2712
  {\scriptsize\raggedright\noindent \mbox{$0{:}\;{-}z_0\,{-}z_1\;0{\to}t_{21}$}\hspace{0.7em plus 0.5em}
2713
  \mbox{$1{:}\;{+}t_{21}\,{-}z_2\;{-}1{\to}t_{21}$}\hspace{0.7em plus 0.5em}
 
7
  \usepackage{booktabs,tabularx,array}
8
  \usepackage{cite}
9
  \usepackage{enumitem}
10
+ \usepackage{tikz}
11
+ \usetikzlibrary{arrows.meta,calc}
12
+ \usepackage{pgfplots}
13
+ \pgfplotsset{compat=1.18}
14
  \usepackage[margin=1in]{geometry}
15
+ \setlength{\emergencystretch}{2em}
16
  \usepackage[colorlinks=true,linkcolor=blue,citecolor=blue,urlcolor=blue]{hyperref}
17
 
18
  % ---- theorem environments ---------------------------------------------------
 
67
  $\{-1,0,1\}$ and integer thresholds, iterated as a fixed composition of ternary
68
  matrix--vector products and Heaviside steps, and construct networks of this kind
69
  that carry out von Neumann's scheme of universal construction and
70
+ self-reproduction exactly, by their dynamics alone. A SUBLEQ computer of
71
+ $17{,}598$ units, one member of a family whose step has depth $13$ at every word
72
+ width, runs a program of $21$ instructions that emits any byte string from a
73
+ run-length description, and one of $27$ instructions that decodes a description
74
+ and then copies it, with which the computer emits the serialization of every bias
75
+ and edge of itself, its memory and its tape. A fixed network holds a stored
76
+ network in its state, as records in two banks with two tapes under three heads,
77
+ and evaluates one record per step; proofs checked in Rocq show that it computes
78
+ the step of this machine from every state at every size. Stored in it as
79
+ $36{,}074$ records of two inputs, the SUBLEQ computer runs the program of $27$
80
+ instructions on a description of its own records: it writes them into the idle
81
+ bank, copies the description and hands control to the copy, which does the same.
82
+ The fixed network thus rebuilds, exactly and in every generation, the weights of
83
+ the stored network that computes, with no map outside it; a constructor of $133$
84
+ records does the same with every step proved in Rocq. Because every
85
+ pre-activation is an integer, every orbit survives perturbations below $1/2$, and
86
+ a bound computed from the pre-activation profile is within $20\%$ of the measured
87
+ failure rate under Gaussian read noise. Replacing each unit by $r$ copies that
88
+ read every copy of their inputs multiplies every margin by $r$, so any noise level
89
+ is survived with redundancy $O(s\sqrt{\log(TM/\delta)})$, and the threefold host
90
+ completes the self-reproducing run at noise levels at which the host fails within
91
+ its first bytes.
 
 
92
  \end{abstract}
93
 
94
  \maketitle
 
118
  behaviour is the orbit of an initial state. The paper treats two questions.
119
  The first is whether such an orbit can emit, exactly, a serialization of the
120
  weights that define $F$ together with the rest of the initial data, so that
121
+ loading the emitted data yields a process that emits the same data. This is the
 
 
 
 
122
  setting of program replicators, which rely on an execution environment to
123
+ instantiate what they emit~\cite{CotlerHonglerHudcova2025}. The second removes
124
+ the environment: whether a fixed network can hold threshold networks in its state
125
+ as data, and whether a universal computer so held can construct its own records
126
+ from a description, copy the description and hand over control to what it built,
127
+ so that the dynamics alone rebuilds the weights of the network that computes.
128
+ This is the setting of von Neumann's cellular constructions, with a threshold
129
+ network as the transition rule and threshold netlists as the configurations: the
130
+ network that reproduces is held as state, and the fixed network that holds it
131
+ plays the part of the cellular rule.
132
 
133
  Integer weights make the reproduction exact. A trained network that outputs an
134
  approximation of its weights loses information in every generation, while a
 
160
  $|f|+\lceil|f|/127\rceil+1$, and none shorter when $f$ has no two equal adjacent
161
  bytes (Section~\ref{sec:univ}).
162
  \item \emph{Exact self-reproduction of the host.} A program of $27$
163
+ instructions, separating construction from copying as von Neumann did, that
164
+ decodes a recipe and then copies it, so that the host emits the serialization of
165
+ itself, its memory and its tape. This is the standard quine scheme run on a
166
+ universal machine; a fixed instantiation map of linear cost, outside the network,
167
+ turns the output into the next running instance (Section~\ref{sec:selfrep}).
 
168
  \item \emph{Self-reproduction by the dynamics.} A family of ternary threshold
169
+ networks $N^\alpha_{G,L}$ whose state holds a stored network as records in two
170
+ banks, with two tapes and three heads, each step evaluating one record; the
171
+ network computes the step of this two-head machine from every state, for every
172
+ window of $2^\alpha$ cells and every $G$ and $L$, a proof checked in Rocq
173
+ (Theorem~\ref{thm:physnet}). Stored in the member with $4096$ cells as
174
+ $36{,}074$ records of two inputs, the units of the host with records that load its
175
+ memory, its tape device and a hand-over, the host running $P^\ast$ builds every
176
+ image of its size from any recipe for it into the idle bank and hands control to
177
+ it, and started on a recipe for its own records it reproduces in every
178
+ generation, weights included, the orbit of the network being periodic
179
+ (Theorems~\ref{thm:storedconstruct} and~\ref{thm:storedrep}); three generations
180
+ of $7.9\times10^{10}$ steps each were run. A constructor of $133$ records with a
181
+ description code of its own does the same in the member with $64$ cells, every
182
+ step of it proved in Rocq (Section~\ref{sec:organism}).
183
  \item \emph{Robustness.} Every orbit survives perturbations below $1/2$ per
184
  pre-activation. The tolerances for weight and leakage errors are given by
185
  fan-in and layer width and, along an orbit, by its active inputs, and a bound
 
205
 
206
  Sections~\ref{sec:prelim} to~\ref{sec:robust} define the networks, build the
207
  host, carry out construction and self-reproduction on it and treat
208
+ perturbations. Section~\ref{sec:interp} gives the two-head machine,
209
+ Section~\ref{sec:storedhost} stores the host in it and shows that it reproduces
210
+ itself by the dynamics, Section~\ref{sec:organism} gives the constructor proved in
211
+ Rocq, and Section~\ref{sec:related} related work. The appendices give the
212
+ measurements in full, what was executed and checked in Rocq with what reproduces
213
+ each published digest, and the listings.
214
 
215
  % =============================================================================
216
  \section{Threshold maps, transducers, and reproduction}\label{sec:prelim}
 
456
  The maps $\inst$ and $\Lev$ together with the device of
457
  Definition~\ref{def:transducer} are the \emph{instantiation map}: the network
458
  emits a description, and $\inst$, $\sigma^{-1}$ and $\Lev$ turn it into the next
459
+ running network. Section~\ref{sec:storedhost} removes the map: the host, held as
460
+ records in the state of a fixed network, carries out the same step by the
461
+ dynamics. With the map, the cost for the host is the following.
462
 
463
  \begin{proposition}[Cost of instantiation]\label{prop:envcost}
464
  Let $\Omega=(N,m,\tau)$ be an instance whose run halts after $T$ steps, and let
 
633
 
634
  \begin{construction}[The host family]\label{con:host}
635
  For $A\ge3$ the netlist $H_A$ has the blocks of Table~\ref{tab:blocks}, in that
636
+ order, each unit after its predecessors. Its state is, in this order, the $A$
637
+ counter bits, a halt bit and the $A\cdot2^A$ memory bits word by word, the counter
638
+ and each word most significant bit first, so $n=A+1+A2^A$; for $A=8$ the counter
639
+ occupies coordinates $0$ to $7$, the halt bit $8$, and bit $k$ of word $j$, $k=0$
640
+ the most significant, coordinate $9+8j+k$. The decode lines $P_j$
641
  of $p$ give the three operands by reindexing, $M[p+o]_t=\bigvee_j(\mu_{j+o,t}\wedge
642
  P_j)$, and the bits of $p+3$ as disjunctions of the lines $P_j$ for which the
643
  corresponding bit of $j+3\bmod 2^A$ is $1$. The decode lines of $a$ and $b$ give
 
955
  \end{construction}
956
 
957
  \begin{theorem}[Exact self-reproduction]\label{thm:selfrep}
958
+ For every recipe $\tau$ the instance $(\Nhost,m^\ast,\tau)$ halts and
959
+ $\Out(\Nhost,m^\ast,\tau)=\delta(\tau)\cat\tau$; its run reads no byte at
960
+ $h=|\tau|$ before it requests the rewind, and requests it at $h=|\tau|$. In
961
+ particular $\Omega^\ast=(\Nhost,m^\ast,\tau^\ast)$ is exactly self-reproducing,
962
+ $\Out(\Omega^\ast)=\ser(\Omega^\ast)$, and the generations
963
  $\Omega_{k+1}=\inst(\Out(\Omega_k))$ starting from $\Omega^\ast$ are constant.
964
  \end{theorem}
965
 
966
  \begin{proof}
967
  Phase~(A) consists of the instructions of $P$ with the same memory layout, and
968
  the cells $\cell{EOK}$ and $c_{\mathrm{eot}}$ are not addressed by them, so the
969
+ proof of Theorem~\ref{thm:univ} applies: every token of $\tau$ is consumed in
970
+ order, no byte is read after the end token, and when the end token is read the
971
+ output word is $\delta(\tau)$, the head is at $|\tau|$, $M[\cell{Z}]=0$,
972
+ $M[R_{\mathrm{out}}]=0$, $M[\cell{EOK}]=1$, and all request bits are $0$.
973
+ Phase~(R) assigns $M[c_{\mathrm{rw}}]\leftarrow 1$, the result being positive, and
974
+ the device sets $h=0$ and clears $c_{\mathrm{rw}}$.
 
975
 
976
  For phase~(B) consider the assertion
977
  \[
978
  J_i:\quad
979
  \begin{minipage}{0.78\textwidth}
980
  control reaches the head of phase~(B) with head position $i$, output word
981
+ $\omega=\delta(\tau)\cat\tau_0\cdots\tau_{i-1}$,
982
  $M[\cell{Z}]=0$, $M[\cell{EOK}]=1$, $M[R_{\mathrm{out}}]=0$, all request bits $0$.
983
  \end{minipage}
984
  \]
985
  $J_0$ holds. Assume $J_i$; the first instruction requests a read. If
986
+ $i<|\tau|$, the device loads $\tau_i$, clears $c_{\mathrm{eot}}$ and
987
  advances the head; the second instruction leaves $\cell{EOK}=1$, positive; the
988
+ third and fourth assign $M[\cell{Z}]\leftarrow\nu(\tau_i)$ and
989
+ $M[R_{\mathrm{out}}]\leftarrow\tau_i$; the fifth emits $\tau_i$; the
990
  sixth restores $M[\cell{Z}]=0$ and branches back, so $J_{i+1}$ holds. If
991
+ $i=|\tau|$, the device sets $c_{\mathrm{eot}}=1$, the second instruction
992
  assigns $M[\cell{EOK}]\leftarrow0$ and transfers control to $\cell{0xFF}$, and the
993
+ output word is $\delta(\tau)\cat\tau$. For $\tau=\tau^\ast$,
994
+ $\delta(\tau^\ast)=\field{\sigma(\Nhost)}\cat\field{m^\ast}$ by
995
+ Lemma~\ref{lem:describe}, so the output is $\ser(\Omega^\ast)$, and the last
996
  statement is Lemma~\ref{lem:generations}.
997
  \end{proof}
998
 
 
1223
  exact orbit, the profile bound of Proposition~\ref{prop:profile} lies $2\%$ to
1224
  $19\%$ above the measured per-step rate, in the run of Table~\ref{tab:curve} and
1225
  in a repetition with a separate simulator, and $Mp$ exceeds the bound by a factor
1226
+ of $2.7$ (Figure~\ref{fig:noise}(a)). Under read noise on every
1227
  pre-activation the complete self-reproducing run emits the whole instance at
1228
  $s=0.05$ and has its halt bit set after $143$ bytes at $s=0.10$
1229
  (Table~\ref{tab:noise}). Under static weight error the orbit stays exact up to a
 
1231
  thirteen settings of Table~\ref{tab:leak}, Corollary~\ref{cor:mismatch}
1232
  certifies three and Proposition~\ref{prop:orbit} nine.
1233
 
1234
+ \begin{figure}[t]
1235
+ \centering
1236
+ \begin{tikzpicture}
1237
+ \begin{semilogyaxis}[name=a,width=7.4cm,height=5.8cm,font=\footnotesize,
1238
+ xlabel={noise deviation $s$},ylabel={per-step probability},
1239
+ xmin=0.055,xmax=0.155,ymin=1e-13,ymax=1e2,
1240
+ xtick={0.06,0.08,0.10,0.12,0.14},
1241
+ xticklabel style={/pgf/number format/fixed,/pgf/number format/fixed zerofill,/pgf/number format/precision=2},
1242
+ ytick={1e-12,1e-9,1e-6,1e-3,1},
1243
+ legend style={font=\scriptsize,at={(0.97,0.03)},anchor=south east,draw=none,fill=white,fill opacity=0.8,text opacity=1},
1244
+ legend cell align=left,title={(a) $\Lev(\Nhost)$},title style={font=\footnotesize}]
1245
+ \addplot[densely dashed,thick,black!55] coordinates {(0.06,3.30e-12) (0.08,1.72e-5) (0.09,1.16e-3) (0.10,2.41e-2) (0.11,2.30e-1) (0.12,1.30) (0.15,3.60e1)};
1246
+ \addlegendentry{$Mp$}
1247
+ \addplot[thick,black] coordinates {(0.06,1.21e-12) (0.08,6.34e-6) (0.09,4.27e-4) (0.10,8.85e-3) (0.11,8.42e-2) (0.12,4.73e-1) (0.15,1.30e1)};
1248
+ \addlegendentry{profile bound}
1249
+ \addplot[only marks,mark=*,mark size=1.6pt] coordinates {(0.08,6.94e-6) (0.09,4.07e-4) (0.10,8.71e-3) (0.11,7.70e-2) (0.12,3.59e-1) (0.15,1.00)};
1250
+ \addlegendentry{measured}
1251
+ \end{semilogyaxis}
1252
+ \begin{semilogyaxis}[at={(a.east)},anchor=west,xshift=1.0cm,width=7.4cm,height=5.8cm,font=\footnotesize,
1253
+ xlabel={noise deviation $s$},
1254
+ xmin=0.19,xmax=0.37,ymin=1e-13,ymax=1e2,
1255
+ xtick={0.20,0.25,0.30,0.35},
1256
+ xticklabel style={/pgf/number format/fixed,/pgf/number format/fixed zerofill,/pgf/number format/precision=2},
1257
+ ytick={1e-12,1e-9,1e-6,1e-3,1},
1258
+ legend style={font=\scriptsize,at={(0.97,0.03)},anchor=south east,draw=none,fill=white,fill opacity=0.8,text opacity=1},
1259
+ legend cell align=left,title={(b) $N_3$, read by majority},title style={font=\footnotesize}]
1260
+ \addplot[densely dashed,thick,black!55] coordinates {(0.20,8.03e-9) (0.22,1.16e-6) (0.25,2.48e-4) (0.27,3.48e-3) (0.30,7.22e-2) (0.33,6.90e-1) (0.36,3.89)};
1261
+ \addlegendentry{first order}
1262
+ \addplot[thick,black] coordinates {(0.20,3.59e-13) (0.22,1.82e-10) (0.25,1.58e-7) (0.27,4.57e-6) (0.30,2.26e-4) (0.33,4.34e-3) (0.36,4.30e-2)};
1263
+ \addlegendentry{restoration bound}
1264
+ \addplot[only marks,mark=*,mark size=1.6pt] coordinates {(0.27,4.04e-6) (0.30,2.13e-4) (0.33,3.66e-3) (0.36,2.93e-2)};
1265
+ \addlegendentry{measured}
1266
+ \addplot[only marks,mark=o,mark size=1.8pt] coordinates {(0.24,9.79e-7) (0.27,8.47e-5) (0.30,2.46e-3) (0.33,2.58e-2)};
1267
+ \addlegendentry{a copy deviates}
1268
+ \end{semilogyaxis}
1269
+ \end{tikzpicture}
1270
+ \caption{The per-step probability of leaving the orbit under Gaussian read noise
1271
+ of deviation $s$, against the bounds of Section~\ref{sec:robust}; the lines join
1272
+ the values computed at the deviations of Tables~\ref{tab:curve}
1273
+ and~\ref{tab:bound3}. (a)~$\Lev(\Nhost)$: the rate at which noisy copies first
1274
+ leave the exact orbit, the profile bound of Proposition~\ref{prop:profile} and
1275
+ the bound $Mp$ of Proposition~\ref{prop:random}. (b)~$N_3$: the rate at which the
1276
+ state read by majority first leaves the orbit, the restoration bound of
1277
+ Proposition~\ref{prop:restore} and the first-order bound of
1278
+ Theorem~\ref{thm:amplify}(ii), with the rate at which some copy of some state bit
1279
+ first deviates.}
1280
+ \label{fig:noise}
1281
+ \end{figure}
1282
+
1283
  \subsection{Fault tolerance by amplification}\label{sec:amplify}
1284
 
1285
  Theorem~\ref{thm:margin} tolerates perturbations below $\tfrac12$ because every
 
1599
  one, and it exceeds one from $s=0.27$. Along the first $20{,}000$ steps of the
1600
  self-describing instance, weighted by the steps each of the $64$ noisy copies
1601
  ran, $b(t)$ lies $1.06$ to $1.47$ times above the measured failure rates
1602
+ (Table~\ref{tab:bound3}, Figure~\ref{fig:noise}(b)), which are $21$ and $12$ times
1603
+ below the rates at which
1604
  the replicated state left its orbit at $s=0.27$ and $0.30$.
1605
 
1606
 
 
1611
  This section gives a fixed network whose state holds a threshold network as
1612
  data: a program bank lists the units of the stored network as records, the step
1613
  evaluates the record in front of the bank and turns the bank by one record, and
1614
+ three heads read and write a second bank and two tapes
1615
+ (Figure~\ref{fig:machine}).
1616
 
1617
  \begin{definition}[The two-head machine]\label{def:twohead}
1618
+ Let $\alpha\ge3$ and $G,L\ge1$, and put $b=8+3\alpha$ and $N=bG$. A state of the
1619
+ machine $T^\alpha_{G,L}$ consists of a window $w\in\B^{2^\alpha}$, two banks
1620
+ $Q_0,Q_1\in\B^N$ and two tapes $D_0,D_1\in\B^L$, each bank and tape circular and
1621
+ listed from its head. Window cell $0$ is the bank bit $\kappa$; $Q_\kappa$ is the
1622
+ active bank and $Q_{1-\kappa}$ the idle one, $D_\kappa$ the read tape and
1623
+ $D_{1-\kappa}$ the copy tape. Window cells $1$, $2$ and $3$ are not stored: they
1624
+ name the cell under the read head $D_\kappa[0]$, under the arm $Q_{1-\kappa}[0]$
1625
+ and under the copy head $D_{1-\kappa}[0]$, and cells $4$, $5$ and $6$ are the
1626
+ requests that move these three heads. A \emph{record} is a word
1627
+ $(c_0,c_1,\beta,a_0,a_1,a_o)\in\B^{b}$: two weight codes of two bits, read as
1628
  $w=1$ for $01$, $w=-1$ for $10$ and $w=0$ otherwise, a bias $\beta\in[-8,7]$ in
1629
+ two's complement on four bits, and three window addresses of $\alpha$ bits, most
1630
+ significant first. One step reads the record $Q_\kappa[0..b)$ and the values
1631
  $x_a$ of the cells it names, and
1632
  \begin{enumerate}[label=(\roman*)]
1633
  \item computes $v=\Hv(w_0x_{a_0}+w_1x_{a_1}+\beta)$;
1634
  \item writes $v$ into window cell $a_o$ if $a_o=0$ or $a_o\ge4$, under the arm if
1635
  $a_o=2$, under the copy head if $a_o=3$, and nowhere if $a_o=1$;
1636
+ \item turns $Q_\kappa$ by $b$ cells, and the read tape, the idle bank and the copy
1637
  tape by one cell each when cell $4$, $5$ or $6$ holds $1$ after the write;
1638
  \item clears cells $4$, $5$ and $6$.
1639
  \end{enumerate}
1640
+ We write $T_{G,L}$ for $T^6_{G,L}$, whose window has $64$ cells and whose records
1641
+ have $26$ bits.
1642
  \end{definition}
1643
 
1644
+ \begin{figure}[t]
1645
+ \centering
1646
+ \begin{tikzpicture}[font=\footnotesize,>=Stealth,
1647
+ c/.style={draw,minimum height=5.5mm,minimum width=9mm,inner sep=0pt,anchor=west,text height=1.5ex,text depth=0.3ex},
1648
+ s/.style={draw,minimum height=5.5mm,inner sep=0pt,anchor=west,text height=1.5ex,text depth=0.3ex},
1649
+ lab/.style={anchor=east},
1650
+ note/.style={font=\scriptsize,text=black!70}]
1651
+ \node[lab] at (0,0) {window $w$};
1652
+ \foreach \i/\t in {0/{$\kappa$},1/{rd},2/{arm},3/{cpy},4/{AR},5/{AA},6/{AC},7/{sink}} {
1653
+ \node[c] (w\i) at (0.15+0.9*\i,0) {\t};
1654
+ \node[font=\tiny,text=black!60] at ($(w\i.north)+(0,0.13)$) {\i};
1655
+ }
1656
+ \node[c,minimum width=40mm] (wr) at (0.15+0.9*8,0) {cells $8,\dots,2^\alpha-1$};
1657
+ \node[lab] at (0,-1.45) {active bank $Q_\kappa$};
1658
+ \node[s,fill=black!10,minimum width=40mm] (r0) at (0.15,-1.45) {record $(c_0,c_1,\beta,a_0,a_1,a_o)$};
1659
+ \node[s,minimum width=24mm] (r1) at (r0.east) {next record};
1660
+ \node[s,minimum width=48mm] (r2) at (r1.east) {};
1661
+ \node[note,anchor=east] at ($(r2.east)+(-0.1,0)$) {turns one record per step};
1662
+ \draw[->,rounded corners=3pt] (r0.north) -- ++(0,0.42) -| node[pos=0.25,fill=white,inner sep=2pt]
1663
+ {$v=\Hv(w_0x_{a_0}+w_1x_{a_1}+\beta)$ into cell $a_o$} (wr.south);
1664
+ \foreach \y/\n/\l/\k/\q in {-2.35/rt/{read tape $D_\kappa$}/rd/AR, -3.05/ib/{idle bank $Q_{1-\kappa}$}/arm/AA, -3.75/ct/{copy tape $D_{1-\kappa}$}/cpy/AC} {
1665
+ \node[lab] at (0,\y) {\l};
1666
+ \node[s,fill=black!10,minimum width=9mm] (\n0) at (0.15,\y) {\k};
1667
+ \node[s,minimum width=103mm] (\n1) at (\n0.east) {};
1668
+ \node[note,anchor=east] at ($(\n1.east)+(-0.1,0)$) {turns one cell when \q\ holds $1$};
1669
+ }
1670
+ \end{tikzpicture}
1671
+ \caption{The two-head machine $T^\alpha_{G,L}$. A step evaluates the record in
1672
+ front of the active bank on the cells $a_0$ and $a_1$ and writes the result into
1673
+ cell $a_o$. Window cells $1$, $2$ and $3$ are the cells under the read head, the
1674
+ arm and the copy head, shaded, and cells $4$, $5$ and $6$ turn them. The banks
1675
+ and the tapes are circular.}
1676
+ \label{fig:machine}
1677
+ \end{figure}
1678
+
1679
  A \emph{pass} is $G$ steps. The active bank comes back to the record it started
1680
  with, so a pass evaluates the $G$ records of the bank once each, in order, each
1681
  on the values the records before it left. The bank bit is read at the start of a
 
1683
  the next step on.
1684
 
1685
  \begin{theorem}[The machine as a ternary threshold network]\label{thm:physnet}
1686
+ For all $\alpha\ge3$ and $G,L\ge1$ there is a clocked ternary threshold netlist
1687
+ $N^\alpha_{G,L}$ on the $2^\alpha-3+2N+2L$ bits of the stored window cells
1688
+ $0,4,5,\dots,2^\alpha-1$ and of $Q_0$, $Q_1$, $D_0$ and $D_1$, in this order and
1689
+ each from its head, with $8\cdot2^\alpha+9\alpha+81+8N+6L$ units,
1690
+ $(3\alpha+12)2^\alpha+18\alpha+170+18N+12L$ edges, every weight $\pm1$ and depth
1691
+ $15$, such that for every state $x$ of $T^\alpha_{G,L}$ the next state
1692
+ $N^\alpha_{G,L}$ computes from the bits of $x$ is the encoding of the next state
1693
+ of $x$.
1694
  \end{theorem}
1695
 
1696
  \begin{proof}
1697
  The netlist has twenty-eight blocks. A multiplexer on $\kappa$ selects each bit
1698
  of the record in front of the active bank and the cell under each head. Three
1699
+ blocks of $2^\alpha$ units decode $a_0$, $a_1$ and $a_o$ (Lemma~\ref{lem:mem}),
1700
+ and a disjunction of the conjunctions of each decode line with the cell it names
1701
+ reads $x_{a_0}$ and $x_{a_1}$, cells $1$ to $3$ through the head multiplexers.
1702
+ From the weight codes, units for $[w_k=1]\wedge x_{a_k}$ and $[w_k=-1]\wedge
1703
+ x_{a_k}$ give $p=w_0x_{a_0}+w_1x_{a_1}\in[-2,2]$; five units test $p\ge c$ for
1704
+ $c=-2,\dots,2$ and five test $\beta\ge-c$ on the four bits of $\beta$, and $v$ is
1705
+ the disjunction of the five conjunctions, since $p+\beta\ge0$ exactly when some
1706
+ $c$, namely $c=p$, has $p\ge c$ and $\beta\ge-c$. A multiplexer on the decode line
1707
+ of each stored window cell writes $v$ into it, and the three request cells read a
1708
+ unit that never fires. Bit $i$ of a bank is the disjunction of three
1709
+ conjunctions: with the bank active, its bit $i+b\bmod N$; with it idle, bit
1710
+ $i+1\bmod N$ or bit $i$ of the bank after the arm's write, as cell $5$ after the
1711
+ write requests; the cell under the arm after the write is a multiplexer of $v$
1712
+ and the old cell on the decode line of $2$. The tapes are alike, with cells $4$
1713
+ and $6$ and the decode line of $3$. Counting the units and edges of the blocks
1714
+ gives the sizes. The depth of a unit depends only on its block: the record and
1715
+ the cells under the heads are selected at depth $2$, the decode lines lie at
1716
+ depth $3$, the operands at $5$, the products at $6$, the tests $p\ge c$ at $7$
1717
+ and $v$ at $9$, the written window cells, the requests among them, at $11$, the
1718
+ next bank bits at $14$, the signals that turn the tapes at $13$ and the next tape
1719
+ bits at $15$. That the next state is the encoding of the next state of $x$, for
1720
+ all $\alpha$, $G$, $L$ and $x$, is proved in Rocq, with the number of units and
1721
+ the weights: every unit is given a value in terms of $x$, every unit fires to its
1722
+ own value on the values of the units it reads, and induction over the units in
1723
+ their order shows that the netlist computes these values, among them the next
1724
+ state. A decode line is proved for every width through the binary value of an
1725
+ address of $\alpha$ bits.
1726
  \end{proof}
1727
 
1728
  \begin{remark}[Corroboration]\label{rem:physcheck}
1729
+ For $\alpha=6$, $G=133$ and $L=2930$, the member that Section~\ref{sec:organism}
1730
+ uses, $N^6_{G,L}$ has $45{,}891$ units, $99{,}602$ edges and $12{,}837$ state bits;
1731
+ for $\alpha=12$, $G=36{,}074$ and $L=1{,}599{,}768$, the member that
1732
+ Section~\ref{sec:storedhost} uses, it has $22{,}329{,}613$ units,
1733
+ $47{,}964{,}818$ edges and $6{,}378{,}141$ state bits. The generator writes the
1734
+ netlist unit by unit in the order of the Rocq definition, and for $N^6_{133,2930}$
1735
+ and $N^{12}_{5,40}$ its file is byte for byte the netlist extracted from that
1736
+ definition. One step of the netlist agreed with a transcription of
1737
+ Definition~\ref{def:twohead} on $300$ random full states for $N^6_{5,40}$, $20$ for
1738
+ $N^6_{133,2930}$ and $40$ for $N^{12}_{5,40}$, and three generations of the organism
1739
+ of Section~\ref{sec:organism}, $2{,}762{,}676$ steps, were run on $N^6_{133,2930}$ in
1740
  C beside the transcription, the full state compared after every step.
1741
  \end{remark}
1742
 
1743
  % =============================================================================
1744
+ \section{The host reproduces itself by the dynamics}\label{sec:storedhost}
1745
  % =============================================================================
1746
 
1747
+ Inside the two-head machine the instantiation step of Section~\ref{sec:selfrep}
1748
+ is a write into the idle bank followed by a change of $\kappa$. This section
1749
+ stores the host as a network of two-input records in a bank of $T^{12}_{G,L}$,
1750
+ whose window of $4096$ cells holds the host's $2057$ state bits, with a device
1751
+ that writes the bytes the host emits under the arm until the tape is rewound and
1752
+ under the copy head after it. Run on a recipe for its own records, $P^\ast$ then
1753
+ writes the host into the idle bank, copies the recipe and hands control to the
1754
+ copy (Figure~\ref{fig:generation}).
1755
+
1756
+ \begin{lemma}[Stored netlists]\label{lem:feedback}
1757
+ If no record but the last of the active bank writes $\kappa$, a pass evaluates the
1758
+ records in order, each on the window and the cells under the heads as the records
1759
+ before it left them. A clocked netlist whose units have at most two predecessors
1760
+ and whose state bits are window cells is therefore evaluated by one pass of
1761
+ records that list its units in an order in which every unit follows its
1762
+ predecessors, each record writing a cell that holds the unit's value until the
1763
+ last record that reads it, and the record of the next value of each state bit
1764
+ writing that bit after every record that reads it.
1765
+ \end{lemma}
1766
+
1767
+ \begin{proof}
1768
+ The active bank turns by one record per step, and $\kappa$, read at the start of a
1769
+ step, selects it. Induction over the records shows that each reads the values of
1770
+ its predecessors on the current state, a state bit being unchanged until the
1771
+ record of its next value, which follows its last reader.
1772
+ \end{proof}
1773
+
1774
+ Gate-level logic simulation engines evaluate one stored gate per step from
1775
+ operand memory and rank-order the gates so that one pass
1776
+ suffices~\cite{Denneau1982}, and Lemma~\ref{lem:feedback} is the fixed-order
1777
+ sequential schedule of automata networks~\cite{RiosWilsonTheyssier2024c}.
1778
+
1779
+ \begin{lemma}[Compilation]\label{cor:compile}
1780
+ A ternary threshold unit $\Hv(\sum_{i\in P}x_i-\sum_{i\in N}x_i+b)$ is $1$
1781
+ exactly when at least $t=|N|-b$ of its $k$ literals $x_i$ ($i\in P$) and $\neg x_i$
1782
+ ($i\in N$) are true. It is a constant for $t\le0$ and for $t>k$, a disjunction
1783
+ for $t=1$ and a conjunction for $t=k$, each a copy of the literal when $k=1$ and a
1784
+ chain of $k-1$ two-input units otherwise, and for $1<t<k$ it satisfies
1785
+ $T_t(x_1,\dots,x_k)=T_t(x_2,\dots,x_k)\vee(x_1\wedge T_{t-1}(x_2,\dots,x_k))$.
1786
+ Every unit so obtained is a record.
1787
+ \end{lemma}
1788
+
1789
+ \begin{proof}
1790
+ The pre-activation is the number of true literals minus $t$. The unit
1791
+ $\Hv(\pm x\pm y+c)$ with $c\in\{0,-1,-2\}$ is the conjunction of any two literals
1792
+ and with $c\in\{-1,0,1\}$ their disjunction, a copy is $\Hv(x-1)$ or $\Hv(-x)$, and
1793
+ the constants are the disjunction and the conjunction of a literal and its
1794
+ negation; a chain conjoins or disjoins each further literal with the result so
1795
+ far. The recursion holds because at least $t$ of $x_1,\dots,x_k$ are true exactly
1796
+ when at least $t$ of $x_2,\dots,x_k$ are, or $x_1$ is and at least $t-1$ of the
1797
+ others are, and its base cases are the chains and the constants.
1798
+ \end{proof}
1799
+
1800
+ \begin{construction}[The stored host]\label{con:stored}
1801
+ Let $\alpha=12$. The window holds, after the eight cells of
1802
+ Definition~\ref{def:twohead}, a cell $\mathrm{booted}$ at $8$, a counter
1803
+ $\mathrm{cnt}$ of $20$ bits at $9$ to $28$, least significant first, a cell
1804
+ $\mathrm{dest}$ at $29$, a limit $\mathrm{lim}$ of $20$ bits at $30$ to $49$, the
1805
+ $2057$ state bits of $\Nhost$ at $50$ to $2106$ in the order of
1806
+ Construction~\ref{con:host}, and temporaries from $2107$ on. The stored host $S$
1807
+ lists the following $36{,}074$ records.
1808
+ \begin{enumerate}[label=(\alph*),leftmargin=2em]
1809
+ \item \emph{Startup}, $2099$ records: for each memory bit $c$,
1810
+ $c\leftarrow c\vee\neg\mathrm{booted}$ if its value in $m^\ast$ is $1$ and
1811
+ $c\leftarrow c\wedge\mathrm{booted}$ if it is $0$; then
1812
+ $c\leftarrow c\wedge\mathrm{booted}$ for the bits of the counter, the halt bit and
1813
+ the bits of $\mathrm{cnt}$, $\mathrm{lim}$ and $\mathrm{dest}$; then
1814
+ $\mathrm{booted}\leftarrow1$.
1815
+ \item \emph{Step}, $33{,}568$ records: the units of $\Nhost$ compiled by
1816
+ Lemma~\ref{cor:compile}, in two stages. The first evaluates every unit that a
1817
+ next value depends on, other than the units of the next values and the units read
1818
+ by a single unit, each after its predecessors. The second takes the counter bits,
1819
+ the halt bit and the memory bits in this order, and evaluates for each the unit of
1820
+ its next value, with the units read by it alone just before it, its last record
1821
+ writing the bit.
1822
+ \item \emph{Device}, $402$ records: the device of Definition~\ref{def:ring} on the
1823
+ state that~(b) leaves.
1824
+ \item \emph{Hand-over}, five records: $\mathrm{booted}\leftarrow
1825
+ \mathrm{booted}\wedge\neg h$, where $h$ is the halt bit; a record with both
1826
+ weights $0$ that writes $0$ into the sink, which makes $S$ a whole number of bytes;
1827
+ and $\kappa\leftarrow\kappa\oplus h$ in three records.
1828
+ \end{enumerate}
1829
+ Temporaries whose live intervals, from the first write to the last read, are
1830
+ disjoint share a cell, $789$ cells in all, so $S$ uses the window cells $0$ to
1831
+ $2895$. The image of $S$, its records in order, has $1{,}587{,}256$ bits, read as
1832
+ $198{,}407$ bytes most significant bit first, and $\tau_S=\describe(S)$ has
1833
+ $199{,}971$ bytes.
1834
+ \end{construction}
1835
+
1836
+ \begin{definition}[The device of the stored host]\label{def:ring}
1837
+ The device acts on the state $y$ of the host after a step, the registers
1838
+ $\mathrm{cnt}$, $\mathrm{lim}$ and $\mathrm{dest}$, the read tape, the idle bank
1839
+ and the copy tape, each request firing on the value $1$:
1840
+ \begin{enumerate}[label=(\roman*)]
1841
+ \item if $y_{c_{\mathrm{wr}}}=1$, the eight bits of $y_{R_{\mathrm{out}}}$, most
1842
+ significant first, are written under the arm if $\mathrm{dest}=0$ and under the
1843
+ copy head if $\mathrm{dest}=1$, that head turning after each, and
1844
+ $R_{\mathrm{out}}$ is cleared;
1845
+ \item if $y_{c_{\mathrm{rd}}}=1$, then $c_{\mathrm{eot}}$ is set if
1846
+ $\mathrm{dest}=1$ and $\mathrm{cnt}=\mathrm{lim}$, and otherwise the eight bits
1847
+ under the read head, the head turning after each, are loaded into
1848
+ $R_{\mathrm{in}}$ most significant first, $c_{\mathrm{eot}}$ is cleared and
1849
+ $\mathrm{cnt}$ is incremented;
1850
+ \item if $y_{c_{\mathrm{rw}}}=1$, then $\mathrm{lim}\leftarrow\mathrm{cnt}$,
1851
+ $\mathrm{cnt}\leftarrow0$ and $\mathrm{dest}\leftarrow1$;
1852
+ \item the request cells are cleared.
1853
+ \end{enumerate}
1854
+ \end{definition}
1855
+
1856
+ \begin{figure}[t]
1857
+ \centering
1858
+ \begin{tikzpicture}[font=\footnotesize,>=Stealth,
1859
+ s/.style={draw,minimum height=6mm,inner sep=1pt,anchor=west,text height=1.5ex,text depth=0.3ex},
1860
+ lab/.style={anchor=east},
1861
+ note/.style={font=\scriptsize,text=black!70}]
1862
+ \node[lab] at (0,0) {active bank $Q_\kappa$};
1863
+ \node[s,fill=black!10,minimum width=13mm] (a1) at (0.15,0) {startup};
1864
+ \node[s,fill=black!10,minimum width=35mm] (a2) at (a1.east) {step of $\Nhost$};
1865
+ \node[s,fill=black!10,minimum width=13mm] (a3) at (a2.east) {device};
1866
+ \node[s,fill=black!10,minimum width=15mm] (a4) at (a3.east) {hand-over};
1867
+ \node[note,anchor=south] at (a1.north) {$2099$};
1868
+ \node[note,anchor=south] at (a2.north) {$33{,}568$ records};
1869
+ \node[note,anchor=south] at (a3.north) {$402$};
1870
+ \node[note,anchor=south] at (a4.north) {$5$};
1871
+ \node[lab] at (0,-1.1) {read tape $D_\kappa$};
1872
+ \node[s,minimum width=76mm] (d0) at (0.15,-1.1) {$\tau_S=\describe(S)$, $199{,}971$ bytes};
1873
+ \node[lab] at (0,-2.2) {idle bank $Q_{1-\kappa}$};
1874
+ \node[s,densely dashed,minimum width=76mm] (q1) at (0.15,-2.2) {$S$, written under the arm in phase~(A)};
1875
+ \node[lab] at (0,-3.3) {copy tape $D_{1-\kappa}$};
1876
+ \node[s,densely dashed,minimum width=76mm] (d1) at (0.15,-3.3) {$\tau_S$, written under the copy head in phase~(B)};
1877
+ \node[draw,rounded corners,align=left,anchor=north west,text width=31mm,minimum height=39mm,inner sep=4pt]
1878
+ (win) at (9.75,0.3)
1879
+ {\textbf{window}\\[2pt] the host's state, with\\ $P^\ast$ and $m^\ast$ in its\\ memory; the device's\\ $\mathrm{cnt}$, $\mathrm{lim}$, $\mathrm{dest}$;\\ $\mathrm{booted}$; $\kappa$, flipped\\ by the hand-over\\ at the halt};
1880
+ \draw[->] (a4.east) -- node[note,above] {runs} (win.west|-a4.east);
1881
+ \draw[->] (d0.east) -- node[note,above] {(A), (B)} (win.west|-d0.east);
1882
+ \draw[<-] (q1.east) -- node[note,above] {(A)} (win.west|-q1.east);
1883
+ \draw[<-] (d1.east) -- node[note,above] {(B)} (win.west|-d1.east);
1884
+ \end{tikzpicture}
1885
+ \caption{A generation of the stored host. Each pass evaluates the records of $S$
1886
+ and carries out one step of the host on the window. $P^\ast$ reads $\tau_S$
1887
+ twice: in phase~(A) it decodes it, and the device writes the decoded bytes, the
1888
+ image of $S$, under the arm; in phase~(B) it copies $\tau_S$ under the copy head.
1889
+ At the halt the hand-over flips $\kappa$, and the next pass, in the other bank,
1890
+ loads $m^\ast$ and runs the copy.}
1891
+ \label{fig:generation}
1892
+ \end{figure}
1893
+
1894
+ \begin{proposition}[One pass]\label{prop:storedpass}
1895
+ Let a state of $T^{12}_{G,L}$ with $G=36{,}074$ hold $S$ in the active bank from
1896
+ its first record, and in the window clear requests and a host state $x$. One pass
1897
+ replaces $x$ by $x_0(m^\ast)$ and clears $\mathrm{cnt}$, $\mathrm{lim}$ and
1898
+ $\mathrm{dest}$ if $\mathrm{booted}=0$; then carries out one step of $\Nhost$
1899
+ followed by the device of Definition~\ref{def:ring}; then sets $\mathrm{booted}$
1900
+ to the negation of the halt bit and, if the halt bit is $1$, changes $\kappa$ at
1901
+ its last step. It leaves $S$ in the active bank from its first record and the
1902
+ requests clear, whatever the temporaries held.
1903
+ \end{proposition}
1904
+
1905
+ \begin{proof}
1906
+ By Lemma~\ref{lem:feedback} the pass evaluates the blocks of
1907
+ Construction~\ref{con:stored} in order. The startup records leave every cell
1908
+ unchanged when $\mathrm{booted}=1$; otherwise they set each memory bit to its value
1909
+ in $m^\ast$ and clear the other cells they name. The step block is $\Nhost$
1910
+ compiled by Lemma~\ref{cor:compile}, in an order that Lemma~\ref{lem:feedback}
1911
+ admits: the first stage writes temporaries only, and no record reads a state bit
1912
+ after the record that writes its next value, a condition checked over the records.
1913
+ So after the block the state bits hold the next state of $\Nhost$. The device
1914
+ records form the requests $[y_c=1]$ as conjunctions of eight literals, the bit
1915
+ $[\mathrm{cnt}=\mathrm{lim}]$ as a conjunction of twenty equivalences, and its
1916
+ conjunction with $\mathrm{dest}$. They then write each bit of $R_{\mathrm{out}}$
1917
+ under the arm or the copy head by a multiplexer, followed by the request that turns
1918
+ that head, and clear $R_{\mathrm{out}}$ on a write; load each bit under the read
1919
+ head into $R_{\mathrm{in}}$ by a multiplexer on the condition of a read, followed
1920
+ by the request that turns the read tape; set $c_{\mathrm{eot}}$; increment
1921
+ $\mathrm{cnt}$ by carries formed before any of its bits changes; carry out the
1922
+ rewind; and clear the requests: these are clauses (i) to (iv) of
1923
+ Definition~\ref{def:ring}. The hand-over records set $\mathrm{booted}$ and,
1924
+ last, $\kappa$. Every temporary is written in the pass before it is read and is not
1925
+ written after its last read, so a cell shared by temporaries holds, at each read,
1926
+ the value of the temporary read. No record writes the active bank, which turns by
1927
+ $G$ records.
1928
+ \end{proof}
1929
+
1930
+ \begin{theorem}[Universal construction by the dynamics]\label{thm:storedconstruct}
1931
+ Let $X$ be an image of $G=36{,}074$ records, read as $198{,}407$ bytes, let $r$ be
1932
+ a recipe with $\delta(r)=X$, and let $L=8|r|$. Let $x$ be a state of
1933
+ $T^{12}_{G,L}$ with $S$ in the active bank from its first record, $r$ on the read
1934
+ tape from its head, and in the window $\mathrm{booted}=0$ and clear requests,
1935
+ whatever the idle bank, the copy tape and the other cells hold. After as many
1936
+ passes as Proposition~\ref{prop:steps} gives steps for $r$, the idle bank holds
1937
+ $X$ and the copy tape $r$, the active bank holds $S$ and the read tape $r$, each
1938
+ from its head, the window holds $\mathrm{booted}=0$ and clear requests, and
1939
+ $\kappa$ has changed, at the last step of the last pass; from then on
1940
+ $T^{12}_{G,L}$ evaluates the records of $X$.
1941
+ \end{theorem}
1942
+
1943
+ \begin{proof}
1944
+ By Proposition~\ref{prop:storedpass} the first pass loads $x_0(m^\ast)$ and
1945
+ carries out the first step of the host, and each later pass one more step, with
1946
+ the device of Definition~\ref{def:ring}, up to the pass in which the halt bit is
1947
+ set, which clears $\mathrm{booted}$ and changes $\kappa$ at its last step. By
1948
+ Theorem~\ref{thm:selfrep}, $P^\ast$ on $r$ with the device of
1949
+ Definition~\ref{def:transducer} halts after the steps of
1950
+ Proposition~\ref{prop:steps} with output $\delta(r)\cat r$, reads no byte at
1951
+ $h=|r|$ before it requests the rewind, and requests it at $h=|r|$. On this run the
1952
+ two devices agree. A token covers at least one byte in at most two, so
1953
+ $|r|\le2|X|+1<2^{20}$ and $\mathrm{cnt}$ does not wrap. Before the rewind
1954
+ $\mathrm{dest}=0$ and each read loads the next
1955
+ byte of $r$ and increments $\mathrm{cnt}$, so $\mathrm{cnt}=h$; at the rewind the
1956
+ read head has turned $8|r|=L$ cells and is back at its start, and $\mathrm{lim}$
1957
+ becomes $|r|$. After it each read loads the next byte while
1958
+ $\mathrm{cnt}<\mathrm{lim}$ and sets $c_{\mathrm{eot}}$ at $\mathrm{cnt}=|r|$, as
1959
+ Definition~\ref{def:transducer} does at $h=|r|$. The bytes emitted before the
1960
+ rewind, $\delta(r)=X$, are written under the arm, which turns
1961
+ $8\cdot198{,}407=N$ cells and comes back to its start, and those emitted after it,
1962
+ $r$, under the copy head, which turns $L$ cells. The active bank and the read tape
1963
+ are not written, and the read tape has turned $2L$ cells. From the next step on
1964
+ the bank holding $X$ is active, from its first record.
1965
+ \end{proof}
1966
+
1967
+ By Lemma~\ref{lem:complete} every image of $G$ records has a recipe, so the stored
1968
+ host builds every image of its own size, itself among them.
1969
+
1970
+ \begin{theorem}[Self-reproduction of the host by the dynamics]\label{thm:storedrep}
1971
+ Let $L=8|\tau_S|=1{,}599{,}768$ and let $x_0$ be the state of $T^{12}_{G,L}$ with
1972
+ $S$ in $Q_0$, $\tau_S$ on $D_0$, and $Q_1$, $D_1$ and the window clear. For every
1973
+ $g\ge1$, the state after $g$ generations of $2{,}202{,}812$ passes,
1974
+ $79{,}464{,}240{,}088$ applications of $T^{12}_{G,L}$ each, holds $S$ in both banks
1975
+ and $\tau_S$ on both tapes, each from its head, with $\kappa=g\bmod2$, and equals
1976
+ the state after $g+2$ generations. The network $N^{12}_{G,L}$ started from the bits
1977
+ of $x_0$ holds their bits at the same steps, and its orbit is periodic from the
1978
+ first generation on, with a period of two generations.
1979
+ \end{theorem}
1980
+
1981
+ \begin{proof}
1982
+ The first generation is Theorem~\ref{thm:storedconstruct} with $X=S$ and
1983
+ $r=\tau_S$, a recipe for $S$ by Lemma~\ref{lem:describe}. The recipe $\tau_S$ has
1984
+ $1563$ literal tokens and no repeat token, with $f_{\mathrm{lit}}=198{,}407$, so
1985
+ Proposition~\ref{prop:steps} gives $1{,}002{,}983+1+1{,}199{,}828$ passes. After
1986
+ it the idle bank and the copy tape hold $S$ and $\tau_S$, the active bank and the
1987
+ read tape still hold them, and the window holds $\mathrm{booted}=0$ and clear
1988
+ requests, so with the roles of the two banks and the two tapes exchanged the state
1989
+ satisfies the hypothesis of Theorem~\ref{thm:storedconstruct} again; induction over
1990
+ the generations gives the first statement. Generations $g$ and $g+2$ start from
1991
+ states that agree in the banks, the tapes, $\kappa$ and $\mathrm{booted}=0$. In the
1992
+ first pass of a generation the startup records write values that do not depend on
1993
+ the cells they read, and every other cell a record reads is written earlier in the
1994
+ pass, apart from $\kappa$, the cells under the heads and the sink, whose value
1995
+ changes no result; the cells from $2896$ on are never addressed. So the two
1996
+ generations perform the same steps and end in the same state.
1997
+ Theorem~\ref{thm:physnet} carries the statement to the network.
1998
+ \end{proof}
1999
+
2000
+ \begin{remark}[Corroboration]\label{rem:storedcheck}
2001
+ Three generations from $x_0$ were run on a transcription of
2002
+ Definition~\ref{def:twohead} in C, $79{,}464{,}240{,}088$ steps each: after each
2003
+ the new active bank held $S$ and the new read tape $\tau_S$, each from its head,
2004
+ the other bank and tape still held them, $\kappa$ alternated, and the full state
2005
+ after the third generation equalled the state after the first. A transcription in
2006
+ Python agreed with the C program on the full state after three passes and
2007
+ $12{,}345$ further steps. One pass was compared with the startup, one step of
2008
+ $\Nhost$ and the device of Definition~\ref{def:ring} from $300$ random states, a
2009
+ fifth of them not booted and a third with requests holding random bytes, with
2010
+ random tapes, idle bank, registers and temporaries, and agreed in every case; that no
2011
+ record of the step block reads a state bit after the record of its next value, and
2012
+ that no temporary is written after its last read, was checked over the records.
2013
+ \end{remark}
2014
+
2015
+ \begin{remark}\label{rem:storedvn}
2016
+ The network $N^{12}_{G,L}$ is the fixed physics; $S$ carries the constructor,
2017
+ phase~(A) of $P^\ast$, the copier, phase~(B), and the control, its startup and
2018
+ hand-over records; and $\tau_S$ is the description, used twice. The records of $S$ are the
2019
+ units of $\Nhost$, weights and biases, compiled into two-input units, together
2020
+ with its memory image in the startup records, so each generation rebuilds the
2021
+ stored network that computes, its weights included, by the dynamics of a fixed
2022
+ network and with no map outside it. The constructor is a universal computer and
2023
+ the description a recipe of Section~\ref{sec:univ}.
2024
+ \end{remark}
2025
+
2026
+ % =============================================================================
2027
+ \section{A constructor proved in Rocq}\label{sec:organism}
2028
+ % =============================================================================
2029
+
2030
+ Theorem~\ref{thm:storedrep} is proved on paper from Theorem~\ref{thm:physnet} and
2031
+ was run for three generations. This section gives a smaller stored network, the
2032
+ organism, that performs von Neumann's cycle in $T_{G,L}$, the machine of $64$
2033
+ cells, with a description code of its own; every step of its reproduction is
2034
+ proved in Rocq.
2035
 
2036
  \begin{definition}[The description code]\label{def:code}
2037
  A \emph{gate} is a record that computes the conjunction or the disjunction of
 
2053
  \end{definition}
2054
 
2055
  An image of $n$ records of which $e$ are not gates has a description of
2056
+ $22n+8e+4$ bits, and the gates include every unit that Lemma~\ref{cor:compile}
2057
+ produces.
2058
 
2059
  \begin{lemma}[Completeness of the code]\label{lem:code}
2060
  For every image $X$ of whole records, $\describe(X)$ is a description of $X$.
 
2070
  \begin{construction}[The organism]\label{con:organism}
2071
  The organism $\rho$ is the image of the $133$ records listed in
2072
  Appendix~\ref{app:listings}, $3458$ bits. It uses $36$ window cells: the bank bit,
2073
+ the six head and request cells, the sink, thirteen cells $8$ to $20$ that hold the
2074
+ state of a decoder, and fifteen temporaries in cells $21$ to $35$, each written in
2075
+ a pass before it is read in that pass. The decoder's state is a mode $m$,
2076
+ construct ($m=0$) or copy, in cell $8$; a phase, codon C, gate bits G or address
2077
+ bits A, one cell each, $9$, $10$ and $11$; a codon register $c_3c_2c_1c_0$, $c_i$
2078
+ in cell $12+i$; and a counter $z=z_4\cdots z_0$, $z_i$ in cell $16+i$. One pass
2079
+ carries out one step of the decoder, with $r$ the bit under the read head:
2080
  \begin{enumerate}[label=(\roman*),leftmargin=2em]
2081
  \item in phase C, $r$ is consumed and shifted into the codon register, and in copy
2082
  mode copied under the copy head; at $z=0$ the codon decides: at end the mode
 
2117
  are checked in Rocq.
2118
  \end{proof}
2119
 
2120
+ \begin{theorem}[Universal construction by the organism]\label{thm:construct}
2121
  Let $X$ be an image of $133$ records and $d$ a description of $X$ of $L$ bits. Let
2122
  $x$ be a state of $T_{133,L}$ with $\rho$ in the active bank $Q_\kappa$ from its
2123
  first record, $d$ on the read tape from its head, and in the window clear
 
2148
  bits, so in $T_{133,2930}$ the organism builds every image of $133$ gates, and in
2149
  $T_{133,2930+8e}$ every image of $133$ records of which $e$ are not gates.
2150
 
2151
+ \begin{theorem}[Self-reproduction of the organism]\label{thm:organism}
2152
  Let $x_0$ be the state of $T_{133,2930}$ with $\rho$ in $Q_0$, $\describe(\rho)$
2153
  on $D_0$, $Q_1$ and $D_1$ clear, $\kappa=0$, and in the window clear requests and
2154
  the decoder in phase C with $z=3$ in construct mode. For every $g\ge1$, the state
2155
  after $g$ generations of $6924$ passes, $920{,}892$ applications of $T$ each,
2156
  holds $\rho$ in both banks and $\describe(\rho)$ on both tapes, with
2157
+ $\kappa=g\bmod2$. The network $N^6_{133,2930}$ started from the bits of $x_0$ holds
2158
  their bits at the same steps. Its orbit is periodic from the first generation
2159
  on, with a period of two generations.
2160
  \end{theorem}
 
2172
  equal, so the orbit is periodic from the first generation on.
2173
  \end{proof}
2174
 
2175
+ \begin{remark}\label{rem:organismcheck}
2176
+ The description of $\rho$ has $2930$ bits against $3458$ for the bank: a gate is
2177
+ named by four bits and its eight bits are written by the organism. Three
2178
+ generations from $x_0$ were also run on the transcription of
 
 
 
 
 
 
 
 
2179
  Definition~\ref{def:twohead}, each of $920{,}892$ steps, and on the netlist in C
2180
  beside it (Remark~\ref{rem:physcheck}); after each both banks held $\rho$ and
2181
  both tapes $\describe(\rho)$, and the full state after the third generation
2182
+ equalled the state after the first. The Rocq definition of the organism and its
2183
+ description equal those of the transcription, and so do the full states of $T$
2184
+ after $1000$ and $20{,}000$ steps.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2185
  \end{remark}
2186
 
2187
  % =============================================================================
 
2225
  constructions use real-valued weights, and Loom includes a layer that restores
2226
  persistent state to $\pm1$ after numerical drift. The two-head machine of
2227
  Section~\ref{sec:interp} is a fixed ternary network of the same kind, which
2228
+ evaluates any network stored in its own state, and Section~\ref{sec:storedhost}
2229
+ stores a SUBLEQ computer in it.
2230
  Valiant's universal circuits are combinational circuits of size
2231
  $\Theta(n\log n)$ that simulate any circuit of size $n$ given its program bits as
2232
  input~\cite{Valiant1976}; intrinsic simulation between families of automata
 
2298
  the dynamics be simpler than the computation attributed to the
2299
  system~\cite{CotlerHonglerHudcova2025}. In those terms Section~\ref{sec:selfrep}
2300
  is a replicator relative to an instantiation map whose cost
2301
+ Proposition~\ref{prop:envcost} counts, and Sections~\ref{sec:storedhost}
2302
+ and~\ref{sec:organism} remove the map: the constructor is a configuration of a
2303
+ fixed threshold network, and the configuration holds the weights of the network
2304
+ that computes, in Section~\ref{sec:storedhost} the universal computer of
2305
+ Section~\ref{sec:host}.
2306
 
2307
  \subsection{Networks that produce their own parameters}
2308
 
 
2511
  \texttt{src/netlist\_eval.c}, with its own SUBLEQ reference, device, parser and
2512
  levelization, sharing no code with (a)--(c). Evaluators (b)--(d) are built from
2513
  $\sigma(\Nhost)$, and every generation of every run instantiated its evaluator
2514
+ afresh from the bytes the previous generation emitted. The two-head machine was
2515
+ evaluated by transcriptions of Definition~\ref{def:twohead} in Python and in C, by
2516
+ the netlist $N^\alpha_{G,L}$ in Python and in C, and in the Rocq kernel. The
2517
+ stored host is built from $\sigma(\Nhost)$ by \texttt{src/stored\_host.py}, which
2518
+ also compares one pass with the host's step and the device, and its generations
2519
+ are run by \texttt{src/stored\_host\_run.c}, a transcription of
2520
+ Definition~\ref{def:twohead} that decodes the active bank once per change of
2521
+ $\kappa$; the three generations of Remark~\ref{rem:storedcheck} take $9$ minutes.
2522
+ Separate code in \texttt{src/independent/}, written from the definitions of this
2523
+ paper, rebuilds $H_A$ from Construction~\ref{con:host} for $A=3$ to $12$,
2524
+ levelizes $\sigma(\Nhost)$, repeats Tables~\ref{tab:curve} to~\ref{tab:bound3}
2525
  with a simulator in C and on the accelerator, and maps the datapath onto Loihi
2526
  and Akida; its sizes, depths and exact runs are those stated here, and its noisy
2527
  runs agree with Appendix~\ref{app:measure} within sampling error.
2528
 
2529
  The Rocq development, \texttt{rocq/} in the artifact, has $25$ files and
2530
+ $4{,}017$ lines and builds with Rocq 9.1.1 in under four minutes on the machine
2531
+ below, most of it in the kernel's evaluations of the first three generations of
2532
+ the organism. It defines the two-head machine for every address width, its
2533
+ netlist block by block, the organism, the description code and the decoder, and
2534
+ proves Theorem~\ref{thm:physnet} for every address width $\alpha\ge3$, bank
2535
+ length $N\ge8+3\alpha$ and tape length $L\ge1$, together with the number of units
2536
+ and the weights, the machine of $64$ cells being the case $\alpha=6$ by
2537
+ computation; it proves Lemma~\ref{lem:code}, Proposition~\ref{prop:pass},
2538
+ Theorem~\ref{thm:construct} for images of any number of records with an idle bank
2539
+ of their length, and Theorem~\ref{thm:organism}. The file that defines the
2540
  organism and its description is written by \texttt{src/twohead.py}, and the
2541
+ netlist definition, extracted to OCaml, writes for $N^6_{133,2930}$ and
2542
+ $N^{12}_{5,40}$ files byte for byte equal to those that
2543
  \texttt{src/twohead\_net.py} writes.
2544
 
2545
  Besides the runs recorded in the remarks of Sections~\ref{sec:host}
2546
+ to~\ref{sec:organism} and in Appendix~\ref{app:measure}, the following were
2547
  checked. The encoder satisfies $\delta(\describe(f))=f$ within the bound of
2548
  Lemma~\ref{lem:describe} on the $29$ serialized machines of the artifact,
2549
  $971{,}266{,}155$ bytes, and on $68{,}061$ further strings chosen to straddle
 
2557
  $\inst(\ser(\Omega))=\Omega$ on $310$ instances; and rebuilding the host from
2558
  source reproduces its serialization byte for byte. Evaluator~(d) agrees with its
2559
  own reference on $2^{20}$ random full states of the netlist and of its
2560
+ levelization, and $\Lev(\Nhost)$ agrees with the netlist it comes from on random
2561
+ states, every weight in $\Tern$ and every pre-activation an integer.
 
2562
 
2563
  The constructor was run on the $29$ recipes of the artifact's machines on the
2564
  integer reference, emitting $971{,}266{,}155$ bytes in $4{,}714{,}592{,}952$ steps,
 
2595
  Every run of $\Omega^\ast$ produced the same output, of SHA-256 digest
2596
  \texttt{c5950d3f\ldots6e7fb523}, and the artifact~\cite{Norton2026} is identified by
2597
  the digests of $\ser(\Omega^\ast)$, of the host serialization, of the serialization
2598
+ of $N_3$, of $\ser(\Omega^\ast_3)$, of the netlist file of $N^6_{133,2930}$, of
2599
  the organism's record image and its description written as $3458$ and $2930$
2600
+ bytes of value $0$ or $1$, and of the image of the stored host and $\tau_S$ as
2601
+ bytes:
2602
  \begin{center}\ttfamily\small
2603
  \begin{tabular}{@{}l@{\quad}l@{}}
2604
  \normalfont $\ser(\Omega^\ast)$ & c5950d3fec6bcf70e8111586a030d21b2dfa55bd69a1c7fe5f016cb06e7fb523\\
2605
  \normalfont $\sigma(\Nhost)$ & 55c555bd64302190d5bde57ee0cd41b36dac5cf65066d0ed3468567a57e85fa3\\
2606
  \normalfont $\sigma(N_3)$ & 111bb202e6f2728449c6b0761f57a059bc0fb38e3e5ba8447bac6613ec7ce313\\
2607
  \normalfont $\ser(\Omega^\ast_3)$ & 05005d59c373fb4998576ac098093de6df046355348e00370dbdccb1d458e3de\\
2608
+ \normalfont $N^6_{133,2930}$ & 0b86fbe2da1d67beafb252a445738794f4eb35ad4aa8987030c65d3612aa50fe\\
2609
  \normalfont organism $\rho$ & e798ce81da252d6717b0f99321aa401924637b48fe09558a6590006de2e2ffe1\\
2610
+ \normalfont $\describe(\rho)$ & 59c917d4b2602b253bbae19d9866869587abea197730197fee8054acc8781d54\\
2611
+ \normalfont stored host $S$ & 7a769aa264a89860149962d7e98f8b2a5cb34ceb7778e88bcbffdccdc0996154\\
2612
+ \normalfont $\tau_S$ & fb23d9b4b41cd5d0576bce37d0cd53a0f63f33de6d4895f2e784eb1148387185
2613
  \end{tabular}
2614
  \end{center}
2615
+ The layouts behind these digests are the following. The metadata of
2616
+ $\sigma(\Nhost)$ is \texttt{machine} \texttt{subleq8},
2617
+ \texttt{weight\_quantization} \texttt{ternary}, \texttt{signal\_registry} the
2618
+ first string below, the state layout of Construction~\ref{con:host} by first
2619
+ coordinate and width, with the number of words for the memory, and \texttt{io}
2620
+ the second, the addresses of Table~\ref{tab:device}:
2621
+ \begin{center}\ttfamily\small
2622
+ \{"halt":[8,1],"mem":[9,256,8],"pc":[0,8]\}\\
2623
+ \{"c\_eot":250,"c\_rd":252,"c\_rw":251,"c\_wr":249,"halt\_pc":255,"r\_in":253,"r\_out":254\}
2624
+ \end{center}
2625
+ The scalars are the \texttt{I32} tensors
2626
+ \texttt{manifest.addr\_bits}, \texttt{data\_bits}, \texttt{memory\_words},
2627
+ \texttt{pc\_width}, \texttt{state\_bits}, \texttt{units} and \texttt{version},
2628
+ holding $8$, $8$, $256$, $8$, $2057$, $17{,}598$ and $6$, and the vectors are
2629
+ \texttt{net.fanin}, \texttt{net.next} and \texttt{net.pred} of type \texttt{I16}
2630
+ and \texttt{net.bias} of type \texttt{I8}. A netlist file holds little-endian
2631
+ $32$-bit integers: the numbers of state bits, units and edges; the bias of each
2632
+ unit; the fan-in of each unit; one signed entry per edge, its magnitude the index
2633
+ of the predecessor plus one and its sign the weight; and, for each state bit, the
2634
+ index of the signal computing its next value, the state bits being numbered first,
2635
+ in the order of Theorem~\ref{thm:physnet}, and the units after them in the order of
2636
+ the Rocq definition. The records of the organism and of $S$ are laid out as in
2637
+ Definition~\ref{def:twohead}, with the window cells of
2638
+ Constructions~\ref{con:organism} and~\ref{con:stored}; within each block of
2639
+ Construction~\ref{con:stored} the order of the records is that of
2640
+ \texttt{src/stored\_host.py}, which gives each temporary the least cell free over
2641
+ its live interval.
2642
+ One generation of $\Omega^\ast$ takes $0.3$ seconds on~(a), $45$ seconds on~(d)
2643
+ and $9$ minutes on~(b). All figures were
2644
  produced with Python 3.12.3, PyTorch 2.14.0 with CUDA 12.6, NumPy 2.5.3,
2645
  safetensors 0.8.0 and GCC 13.3.0 on Ubuntu 24.04, on an Intel Core i9-12900H
2646
  with $20$ hardware threads and $31$ GiB of memory and an NVIDIA GeForce RTX 3070
2647
  Ti Laptop GPU with $8$ GiB of memory.
2648
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2649
  % =============================================================================
2650
  \section{Program listings and the organism}\label{app:listings}
2651
  % =============================================================================
 
2717
  consists of the $133$ records below, in the order a pass evaluates them; record
2718
  $k$ is written $k{:}\;{\pm}a\,{\pm}b\;\beta{\to}o$, reading window cells $a$ and $b$
2719
  with weights $\pm1$ and writing cell $o$, and a record with one slot has weight
2720
+ $0$, written $00$, in the other, which names the sink, cell $7$. Cell $\kappa$ is
2721
+ $0$; $\mathrm{rd}$, $\mathrm{arm}$ and $\mathrm{cpy}$ are the cells $1$, $2$ and
2722
+ $3$ under the read head, the arm and the copy head, and $\mathrm{AR}$,
2723
+ $\mathrm{AA}$ and $\mathrm{AC}$ the requests $4$, $5$ and $6$ that turn them; $m$
2724
+ is the mode, in cell $8$, $\mathrm{C}$, $\mathrm{G}$ and $\mathrm{A}$ the phases,
2725
+ in cells $9$, $10$ and $11$, $c_i$ bit $i$ of the codon register, in cell $12+i$,
2726
+ and $z_i$ bit $i$ of the counter, in cell $16+i$; and $t_k$ is the temporary in
2727
+ cell $k$.
2728
 
2729
  {\scriptsize\raggedright\noindent \mbox{$0{:}\;{-}z_0\,{-}z_1\;0{\to}t_{21}$}\hspace{0.7em plus 0.5em}
2730
  \mbox{$1{:}\;{+}t_{21}\,{-}z_2\;{-}1{\to}t_{21}$}\hspace{0.7em plus 0.5em}
rocq/Assumptions.v CHANGED
@@ -2,7 +2,9 @@
2
  From TH Require Import PhysMain PhysFacts NetMain Complete Run PassLemma Main SelfRep.
3
 
4
  Print Assumptions PhysMain.netlist_step.
 
5
  Print Assumptions PhysFacts.phys_size.
 
6
  Print Assumptions PhysFacts.phys_ternary.
7
  Print Assumptions NetMain.network_orbit.
8
  Print Assumptions NetMain.network_reproduces.
 
2
  From TH Require Import PhysMain PhysFacts NetMain Complete Run PassLemma Main SelfRep.
3
 
4
  Print Assumptions PhysMain.netlist_step.
5
+ Print Assumptions PhysMain.netlist_step64.
6
  Print Assumptions PhysFacts.phys_size.
7
+ Print Assumptions PhysFacts.phys_size64.
8
  Print Assumptions PhysFacts.phys_ternary.
9
  Print Assumptions NetMain.network_orbit.
10
  Print Assumptions NetMain.network_reproduces.
rocq/NetMain.v CHANGED
@@ -2,56 +2,67 @@
2
  reproduces in every generation. *)
3
  From Stdlib Require Import List Bool Arith ZArith Lia.
4
  Import ListNotations.
5
- From TH Require Import TwoHead Tapes Iter Abstract Organism Net Phys PhysSem PhysMain Main.
 
 
 
 
 
 
 
6
 
7
  (* the network iterated on its state bits *)
8
- Fixpoint netiter (N L n : nat) (x : list bool) : list bool :=
9
- match n with O => x | S m => netiter N L m (netstep (nst N L) (phys_units N L) (phys_next N L) x) end.
 
 
 
10
 
11
- Definition shaped_st (N L : nat) (s : st) : Prop :=
12
- length (win s) = 64 /\ length (contents (q0 s)) = N /\ length (contents (q1 s)) = N
13
  /\ length (contents (d0 s)) = L /\ length (contents (d1 s)) = L.
14
 
15
- Lemma step_shaped : forall N L s, 26 <= N -> 1 <= L -> shaped_st N L s -> shaped_st N L (step s).
 
16
  Proof.
17
- intros N L s HN HL (Hw & H0 & H1 & H2 & H3).
18
- destruct (step_eq N L s HN HL H0 H1 H2 H3 Hw) as (qa' & Hqa & Hst).
19
- pose proof (tapes_step N L s HN HL H0 H1 H2 H3 Hw qa' Hqa) as T. cbv zeta in T.
20
  rewrite <- Hst in T. destruct T as (T0 & T1 & T2 & T3).
21
  refine (conj _ (conj _ (conj _ (conj _ _)))).
22
  - rewrite Hst, win_stepres. unfold clearq. rewrite !upd_length, length_wnext. exact Hw.
23
- - rewrite T0. destruct (kk s), (advA s); rewrite ?rot1_length, ?length_wrote, ?rotn_length; assumption.
24
- - rewrite T1. destruct (kk s), (advA s); rewrite ?rot1_length, ?length_wrote, ?rotn_length; assumption.
25
- - rewrite T2. destruct (kk s), (advC s), (advR s); rewrite ?rot1_length, ?length_wrotec; assumption.
26
- - rewrite T3. destruct (kk s), (advC s), (advR s); rewrite ?rot1_length, ?length_wrotec; assumption.
27
  Qed.
28
 
29
  (* every orbit of the network on encoded states is the encoding of the machine's orbit *)
30
- Theorem network_orbit : forall N L n s, 26 <= N -> 1 <= L -> shaped_st N L s ->
31
- netiter N L n (encode s) = encode (itern n s).
32
  Proof.
33
- intros N L n. induction n as [|n IH]; intros s HN HL Hs; [reflexivity|].
34
- cbn [netiter itern]. pose proof Hs as (Hw & H0 & H1 & H2 & H3).
35
- rewrite (netlist_step N L s HN HL Hw H0 H1 H2 H3).
36
- apply IH; [exact HN | exact HL |]. apply step_shaped; [exact HN | exact HL | exact Hs].
37
  Qed.
38
 
39
- Lemma s0_shaped : shaped_st (26 * 133) 2930 s0.
40
  Proof.
41
  unfold shaped_st, s0, tape_of, contents. cbn [win q0 q1 d0 d1 fr bk rev]. rewrite !app_nil_r.
42
  rewrite !repeat_length, length_rho, length_desc. repeat split; reflexivity.
43
  Qed.
44
 
45
- (* The network on 26 * 133 bank bits and 2930 description bits, started from the encoding of
46
- s0, holds after g + 1 generations the encoding of a state in which the bank bit is even g,
47
- both banks hold the organism and both description tapes its description. *)
48
  Theorem network_reproduces : forall g,
49
- exists t, netiter (26 * 133) 2930 (S g * gen_steps) (encode s0) = encode t
50
  /\ wget (win t) KAPPA = Nat.even g
51
  /\ contents (q0 t) = rho /\ contents (q1 t) = rho
52
  /\ contents (d0 t) = desc /\ contents (d1 t) = desc.
53
  Proof.
54
  intro g. exists (itern (S g * gen_steps) s0). split.
55
- - apply network_orbit; [lia | lia | exact s0_shaped].
56
  - exact (every_generation g).
57
  Qed.
 
2
  reproduces in every generation. *)
3
  From Stdlib Require Import List Bool Arith ZArith Lia.
4
  Import ListNotations.
5
+ From TH Require Import TwoHead Tapes Iter Abstract Organism Net Phys PhysWF PhysSem PhysMain Main.
6
+
7
+ (* the machine with a window of 2^al cells, iterated *)
8
+ Fixpoint iterA (al n : nat) (s : st) : st :=
9
+ match n with O => s | S m => iterA al m (stepA al s) end.
10
+
11
+ Lemma iterA_6 : forall n s, iterA 6 n s = itern n s.
12
+ Proof. induction n as [|n IH]; intro s; [reflexivity|]. cbn [iterA itern]. rewrite stepA_6. apply IH. Qed.
13
 
14
  (* the network iterated on its state bits *)
15
+ Fixpoint netiter (al N L n : nat) (x : list bool) : list bool :=
16
+ match n with
17
+ | O => x
18
+ | S m => netiter al N L m (netstep (nst al N L) (phys_units al N L) (phys_next al N L) x)
19
+ end.
20
 
21
+ Definition shaped_st (al N L : nat) (s : st) : Prop :=
22
+ length (win s) = W al /\ length (contents (q0 s)) = N /\ length (contents (q1 s)) = N
23
  /\ length (contents (d0 s)) = L /\ length (contents (d1 s)) = L.
24
 
25
+ Lemma step_shaped : forall al N L s, 3 <= al -> recw al <= N -> 1 <= L ->
26
+ shaped_st al N L s -> shaped_st al N L (stepA al s).
27
  Proof.
28
+ intros al N L s Hal HN HL (Hw & H0 & H1 & H2 & H3).
29
+ destruct (step_eq al N L s Hal HN HL H0 H1 H2 H3 Hw) as (qa' & Hqa & Hst).
30
+ pose proof (tapes_step al N L s Hal HN HL H0 H1 H2 H3 Hw qa' Hqa) as T. cbv zeta in T.
31
  rewrite <- Hst in T. destruct T as (T0 & T1 & T2 & T3).
32
  refine (conj _ (conj _ (conj _ (conj _ _)))).
33
  - rewrite Hst, win_stepres. unfold clearq. rewrite !upd_length, length_wnext. exact Hw.
34
+ - rewrite T0. destruct (kk s), (advA al s); rewrite ?rot1_length, ?length_wrote, ?rotn_length; assumption.
35
+ - rewrite T1. destruct (kk s), (advA al s); rewrite ?rot1_length, ?length_wrote, ?rotn_length; assumption.
36
+ - rewrite T2. destruct (kk s), (advC al s), (advR al s); rewrite ?rot1_length, ?length_wrotec; assumption.
37
+ - rewrite T3. destruct (kk s), (advC al s), (advR al s); rewrite ?rot1_length, ?length_wrotec; assumption.
38
  Qed.
39
 
40
  (* every orbit of the network on encoded states is the encoding of the machine's orbit *)
41
+ Theorem network_orbit : forall al N L n s, 3 <= al -> recw al <= N -> 1 <= L -> shaped_st al N L s ->
42
+ netiter al N L n (encode al s) = encode al (iterA al n s).
43
  Proof.
44
+ intros al N L n. induction n as [|n IH]; intros s Hal HN HL Hs; [reflexivity|].
45
+ cbn [netiter iterA]. pose proof Hs as (Hw & H0 & H1 & H2 & H3).
46
+ rewrite (netlist_step al N L s Hal HN HL Hw H0 H1 H2 H3).
47
+ apply IH; [exact Hal | exact HN | exact HL |]. apply step_shaped; assumption.
48
  Qed.
49
 
50
+ Lemma s0_shaped : shaped_st 6 (26 * 133) 2930 s0.
51
  Proof.
52
  unfold shaped_st, s0, tape_of, contents. cbn [win q0 q1 d0 d1 fr bk rev]. rewrite !app_nil_r.
53
  rewrite !repeat_length, length_rho, length_desc. repeat split; reflexivity.
54
  Qed.
55
 
56
+ (* The network of the machine with 64 cells, on 26 * 133 bank bits and 2930 description bits,
57
+ started from the encoding of s0, holds after g + 1 generations the encoding of a state in which
58
+ the bank bit is even g, both banks hold the organism and both description tapes its description. *)
59
  Theorem network_reproduces : forall g,
60
+ exists t, netiter 6 (26 * 133) 2930 (S g * gen_steps) (encode 6 s0) = encode 6 t
61
  /\ wget (win t) KAPPA = Nat.even g
62
  /\ contents (q0 t) = rho /\ contents (q1 t) = rho
63
  /\ contents (d0 t) = desc /\ contents (d1 t) = desc.
64
  Proof.
65
  intro g. exists (itern (S g * gen_steps) s0). split.
66
+ - rewrite <- iterA_6. apply network_orbit; [lia | unfold recw; lia | lia | exact s0_shaped].
67
  - exact (every_generation g).
68
  Qed.
rocq/Phys.v CHANGED
@@ -1,19 +1,23 @@
1
  (* The two-head physics as a netlist of ternary threshold units, unit for unit in the order of
2
- the generator, for banks of N bits and description tapes of L bits. *)
3
  From Stdlib Require Import List Bool Arith ZArith Lia.
4
  Import ListNotations.
5
  From TH Require Import TwoHead Tapes Net Gates.
6
 
7
  Section Phys.
8
- Variables N L : nat.
9
 
10
- (* the state: the 61 window cells other than 1, 2 and 3, then Q0, Q1, D0, D1 *)
 
 
 
 
11
  Definition realcell (j : nat) : nat := if Nat.eqb j 0 then 0 else j + 3.
12
- Definition oq0 : nat := 61.
13
- Definition oq1 : nat := 61 + N.
14
- Definition od0 : nat := 61 + 2 * N.
15
- Definition od1 : nat := 61 + 2 * N + L.
16
- Definition nst : nat := 61 + 2 * N + 2 * L.
17
 
18
  Definition kap : sref := SIn 0.
19
  Definition fld (j : nat) : sref := SRef 0 j 2.
@@ -21,7 +25,7 @@ Definition fld (j : nat) : sref := SRef 0 j 2.
21
  Definition head_src (j : nat) : nat * nat :=
22
  match j with 0 => (od1, od0) | 1 => (oq0, oq1) | _ => (od0, od1) end.
23
 
24
- Definition bitsof (a t : nat) : bool := Nat.testbit a (5 - t).
25
 
26
  Definition cellref (a : nat) : sref :=
27
  if Nat.eqb a 1 || Nat.eqb a 2 || Nat.eqb a 3 then SRef 1 (a - 1) 2
@@ -35,16 +39,16 @@ Definition hatq (bh base m : nat) : sref := if Nat.eqb m 0 then SRef bh 0 5 else
35
  Definition hatd (dh base m : nat) : sref := if Nat.eqb m 0 then SRef dh 0 6 else SIn (base + m).
36
 
37
  (* 0: the record in front of the active bank *)
38
- Definition blk_fld : block := Blk 26 3 (fun j => mux3 0 j 0 kap (SIn (oq1 + j)) (SIn (oq0 + j))).
39
  (* 1: the cells under the read head, the arm and the copy head *)
40
  Definition blk_head : block :=
41
  Blk 3 3 (fun j => mux3 1 j 0 kap (SIn (fst (head_src j))) (SIn (snd (head_src j)))).
42
  (* 2, 3, 4: the decode lines of the two operand addresses and the output address *)
43
  Definition blk_dec (o : nat) : block :=
44
- Blk 64 1 (fun a => [sAND (map (fun t => (fld (o + t), bitsof a t)) (seq 0 6))]).
45
  (* 5 to 8: the two operands *)
46
- Definition blk_term (bdec : nat) : block := Blk 64 1 (fun a => [sAND [(SRef bdec a 0, true); (cellref a, true)]]).
47
- Definition blk_xs (bterm : nat) : block := Blk 1 1 (fun _ => [sOR (map (fun a => (SRef bterm a 0, true)) (seq 0 64))]).
48
  (* 9 to 17: the weights, the bias and the threshold *)
49
  Definition blk_pos : block := Blk 2 1 (fun i => [sAND [(fld (2 * i), false); (fld (2 * i + 1), true)]]).
50
  Definition blk_neg : block := Blk 2 1 (fun i => [sAND [(fld (2 * i), true); (fld (2 * i + 1), false)]]).
@@ -63,14 +67,14 @@ Definition blk_bge : block :=
63
  Definition blk_vand : block := Blk 5 1 (fun i => [sAND [(SRef 13 i 0, true); (SRef 15 0 (vand_bge i), true)]]).
64
  Definition blk_v : block := Blk 1 1 (fun _ => [sOR (map (fun i => (SRef 16 i 0, true)) (seq 0 5))]).
65
  (* 18, 19: the window after the write, and the cleared request cells *)
66
- Definition blk_xhat : block := Blk 61 3 (fun j => mux3 18 j 0 (SRef 4 (realcell j) 0) (SRef 17 0 0) (SIn j)).
67
  Definition blk_zero : block := Blk 1 1 (fun _ => [sOR []]).
68
  (* 20 to 23: the banks; idlep is the value of kappa for which the bank is idle *)
69
  Definition blk_bankh (bh : nat) (idlep : bool) (base : nat) : block :=
70
  Blk 1 6 (fun _ => [sAND [(kap, idlep); (SRef 18 2 2, true)]; sAND [(kap, idlep); (SRef 18 2 2, false)];
71
  sAND [(SRef 4 2 0, true); (kap, idlep)]] ++ mux3 bh 0 3 (SRef bh 0 2) (SRef 17 0 0) (SIn base)).
72
  Definition blk_bank (bb bh : nat) (idlep : bool) (base : nat) : block :=
73
- Blk N 4 (fun i => [sAND [(kap, negb idlep); (SIn (base + (i + 26) mod N), true)];
74
  sAND [(SRef bh 0 0, true); (hatq bh base ((i + 1) mod N), true)];
75
  sAND [(SRef bh 0 1, true); (hatq bh base i, true)];
76
  sOR [(SRef bb i 0, true); (SRef bb i 1, true); (SRef bb i 2, true)]]).
@@ -83,14 +87,14 @@ Definition blk_desc (db dh base : nat) : block :=
83
  Blk L 3 (fun i => mux3 db i 0 (SRef dh 0 2) (hatd dh base ((i + 1) mod L)) (hatd dh base i)).
84
 
85
  Definition phys_blocks : list block :=
86
- [blk_fld; blk_head; blk_dec 8; blk_dec 14; blk_dec 20; blk_term 2; blk_xs 5; blk_term 3; blk_xs 7;
87
- blk_pos; blk_neg; blk_mt; blk_nt; blk_pge; blk_b4; blk_bge; blk_vand; blk_v; blk_xhat; blk_zero;
88
  blk_bankh 20 true oq0; blk_bank 21 20 true oq0; blk_bankh 22 false oq1; blk_bank 23 22 false oq1;
89
  blk_desch 24 true od0; blk_desc 25 24 od0; blk_desch 26 false od1; blk_desc 27 26 od1].
90
 
91
  (* the unit computing the next value of each state bit *)
92
  Definition out_ref (i : nat) : sref :=
93
- if i <? 61 then (if Nat.eqb i 1 || Nat.eqb i 2 || Nat.eqb i 3 then SRef 19 0 0 else SRef 18 i 2)
94
  else if i <? oq1 then SRef 21 (i - oq0) 3
95
  else if i <? od0 then SRef 23 (i - oq1) 3
96
  else if i <? od1 then SRef 25 (i - od0) 2
 
1
  (* The two-head physics as a netlist of ternary threshold units, unit for unit in the order of
2
+ the generator, for a window of 2^al cells, banks of N bits and description tapes of L bits. *)
3
  From Stdlib Require Import List Bool Arith ZArith Lia.
4
  Import ListNotations.
5
  From TH Require Import TwoHead Tapes Net Gates.
6
 
7
  Section Phys.
8
+ Variables al N L : nat.
9
 
10
+ (* the window has W cells; all but cells 1, 2 and 3 are state bits *)
11
+ Definition W : nat := 2 ^ al.
12
+ Definition nwin : nat := W - 3.
13
+
14
+ (* the state: the window cells other than 1, 2 and 3, then Q0, Q1, D0, D1 *)
15
  Definition realcell (j : nat) : nat := if Nat.eqb j 0 then 0 else j + 3.
16
+ Definition oq0 : nat := nwin.
17
+ Definition oq1 : nat := nwin + N.
18
+ Definition od0 : nat := nwin + 2 * N.
19
+ Definition od1 : nat := nwin + 2 * N + L.
20
+ Definition nst : nat := nwin + 2 * N + 2 * L.
21
 
22
  Definition kap : sref := SIn 0.
23
  Definition fld (j : nat) : sref := SRef 0 j 2.
 
25
  Definition head_src (j : nat) : nat * nat :=
26
  match j with 0 => (od1, od0) | 1 => (oq0, oq1) | _ => (od0, od1) end.
27
 
28
+ Definition bitsof (a t : nat) : bool := Nat.testbit a (al - 1 - t).
29
 
30
  Definition cellref (a : nat) : sref :=
31
  if Nat.eqb a 1 || Nat.eqb a 2 || Nat.eqb a 3 then SRef 1 (a - 1) 2
 
39
  Definition hatd (dh base m : nat) : sref := if Nat.eqb m 0 then SRef dh 0 6 else SIn (base + m).
40
 
41
  (* 0: the record in front of the active bank *)
42
+ Definition blk_fld : block := Blk (recw al) 3 (fun j => mux3 0 j 0 kap (SIn (oq1 + j)) (SIn (oq0 + j))).
43
  (* 1: the cells under the read head, the arm and the copy head *)
44
  Definition blk_head : block :=
45
  Blk 3 3 (fun j => mux3 1 j 0 kap (SIn (fst (head_src j))) (SIn (snd (head_src j)))).
46
  (* 2, 3, 4: the decode lines of the two operand addresses and the output address *)
47
  Definition blk_dec (o : nat) : block :=
48
+ Blk W 1 (fun a => [sAND (map (fun t => (fld (o + t), bitsof a t)) (seq 0 al))]).
49
  (* 5 to 8: the two operands *)
50
+ Definition blk_term (bdec : nat) : block := Blk W 1 (fun a => [sAND [(SRef bdec a 0, true); (cellref a, true)]]).
51
+ Definition blk_xs (bterm : nat) : block := Blk 1 1 (fun _ => [sOR (map (fun a => (SRef bterm a 0, true)) (seq 0 W))]).
52
  (* 9 to 17: the weights, the bias and the threshold *)
53
  Definition blk_pos : block := Blk 2 1 (fun i => [sAND [(fld (2 * i), false); (fld (2 * i + 1), true)]]).
54
  Definition blk_neg : block := Blk 2 1 (fun i => [sAND [(fld (2 * i), true); (fld (2 * i + 1), false)]]).
 
67
  Definition blk_vand : block := Blk 5 1 (fun i => [sAND [(SRef 13 i 0, true); (SRef 15 0 (vand_bge i), true)]]).
68
  Definition blk_v : block := Blk 1 1 (fun _ => [sOR (map (fun i => (SRef 16 i 0, true)) (seq 0 5))]).
69
  (* 18, 19: the window after the write, and the cleared request cells *)
70
+ Definition blk_xhat : block := Blk nwin 3 (fun j => mux3 18 j 0 (SRef 4 (realcell j) 0) (SRef 17 0 0) (SIn j)).
71
  Definition blk_zero : block := Blk 1 1 (fun _ => [sOR []]).
72
  (* 20 to 23: the banks; idlep is the value of kappa for which the bank is idle *)
73
  Definition blk_bankh (bh : nat) (idlep : bool) (base : nat) : block :=
74
  Blk 1 6 (fun _ => [sAND [(kap, idlep); (SRef 18 2 2, true)]; sAND [(kap, idlep); (SRef 18 2 2, false)];
75
  sAND [(SRef 4 2 0, true); (kap, idlep)]] ++ mux3 bh 0 3 (SRef bh 0 2) (SRef 17 0 0) (SIn base)).
76
  Definition blk_bank (bb bh : nat) (idlep : bool) (base : nat) : block :=
77
+ Blk N 4 (fun i => [sAND [(kap, negb idlep); (SIn (base + (i + recw al) mod N), true)];
78
  sAND [(SRef bh 0 0, true); (hatq bh base ((i + 1) mod N), true)];
79
  sAND [(SRef bh 0 1, true); (hatq bh base i, true)];
80
  sOR [(SRef bb i 0, true); (SRef bb i 1, true); (SRef bb i 2, true)]]).
 
87
  Blk L 3 (fun i => mux3 db i 0 (SRef dh 0 2) (hatd dh base ((i + 1) mod L)) (hatd dh base i)).
88
 
89
  Definition phys_blocks : list block :=
90
+ [blk_fld; blk_head; blk_dec 8; blk_dec (8 + al); blk_dec (8 + 2 * al); blk_term 2; blk_xs 5; blk_term 3;
91
+ blk_xs 7; blk_pos; blk_neg; blk_mt; blk_nt; blk_pge; blk_b4; blk_bge; blk_vand; blk_v; blk_xhat; blk_zero;
92
  blk_bankh 20 true oq0; blk_bank 21 20 true oq0; blk_bankh 22 false oq1; blk_bank 23 22 false oq1;
93
  blk_desch 24 true od0; blk_desc 25 24 od0; blk_desch 26 false od1; blk_desc 27 26 od1].
94
 
95
  (* the unit computing the next value of each state bit *)
96
  Definition out_ref (i : nat) : sref :=
97
+ if i <? nwin then (if Nat.eqb i 1 || Nat.eqb i 2 || Nat.eqb i 3 then SRef 19 0 0 else SRef 18 i 2)
98
  else if i <? oq1 then SRef 21 (i - oq0) 3
99
  else if i <? od0 then SRef 23 (i - oq1) 3
100
  else if i <? od1 then SRef 25 (i - od0) 2
rocq/PhysFacts.v CHANGED
@@ -3,14 +3,18 @@ From Stdlib Require Import List Bool Arith ZArith Lia.
3
  Import ListNotations.
4
  From TH Require Import TwoHead Net Gates Phys PhysWF.
5
 
6
- Lemma phys_size : forall N L, length (phys_units N L) = 647 + 8 * N + 6 * L.
7
  Proof.
8
- intros N L. unfold phys_units. rewrite length_compile by apply phys_shaped.
9
  rewrite phys_length. cbn [phys_blocks boff bcount bsize blk_fld blk_head blk_dec blk_term blk_xs blk_pos
10
  blk_neg blk_mt blk_nt blk_pge blk_b4 blk_bge blk_vand blk_v blk_xhat blk_zero blk_bankh blk_bank
11
- blk_desch blk_desc]. lia.
12
  Qed.
13
 
 
 
 
 
14
  Definition ter (u : su) : Prop := forall x w, In (x, w) (sin u) -> w = 1%Z \/ w = (-1)%Z.
15
 
16
  Lemma ter_lits : forall b ls, ter (SU b (map lw ls)).
@@ -35,9 +39,9 @@ Proof.
35
  - destruct r; [exact Hu | apply IH].
36
  Qed.
37
 
38
- Lemma ter_shapes : forall N L b j, Forall ter (bshape (nth b (phys_blocks N L) blk0) j).
39
  Proof.
40
- intros N L b j.
41
  do 28 (destruct b as [|b];
42
  [ cbn [nth phys_blocks bshape blk_fld blk_head blk_dec blk_term blk_xs blk_pos blk_neg blk_mt blk_nt
43
  blk_pge blk_b4 blk_bge blk_vand blk_v blk_xhat blk_zero blk_bankh blk_bank blk_desch blk_desc];
@@ -50,10 +54,10 @@ Proof.
50
  Qed.
51
 
52
  (* every unit of the netlist reads its predecessors with weight +1 or -1 *)
53
- Theorem phys_ternary : forall N L u, In u (phys_units N L) ->
54
  forall i w, In (i, w) (tin u) -> w = 1%Z \/ w = (-1)%Z.
55
  Proof.
56
- intros N L u Hu i w Hin. unfold phys_units, compile in Hu.
57
  apply in_concat in Hu as (us & Hus & Hu). apply in_map_iff in Hus as (blk & <- & Hblk).
58
  unfold flat_block in Hu. apply in_concat in Hu as (us & Hus & Hu).
59
  apply in_map_iff in Hus as (j & <- & Hj). apply in_map_iff in Hu as (v & <- & Hv).
@@ -61,5 +65,5 @@ Proof.
61
  cbn [fst snd] in E. injection E as _ <-.
62
  apply In_nth with (d := blk0) in Hblk as (b & _ & <-).
63
  apply In_nth with (d := su0) in Hv as (r & _ & <-).
64
- exact (ter_nth _ r (ter_shapes N L b j) x w' Hin).
65
  Qed.
 
3
  Import ListNotations.
4
  From TH Require Import TwoHead Net Gates Phys PhysWF.
5
 
6
+ Lemma phys_size : forall al N L, 3 <= al -> length (phys_units al N L) = 8 * 2 ^ al + 9 * al + 81 + 8 * N + 6 * L.
7
  Proof.
8
+ intros al N L Hal. pose proof (W_ge al Hal) as HW. unfold phys_units. rewrite length_compile by apply phys_shaped.
9
  rewrite phys_length. cbn [phys_blocks boff bcount bsize blk_fld blk_head blk_dec blk_term blk_xs blk_pos
10
  blk_neg blk_mt blk_nt blk_pge blk_b4 blk_bge blk_vand blk_v blk_xhat blk_zero blk_bankh blk_bank
11
+ blk_desch blk_desc]. unfold nwin, recw, W in *. lia.
12
  Qed.
13
 
14
+ (* the machine of 64 cells *)
15
+ Corollary phys_size64 : forall N L, length (phys_units 6 N L) = 647 + 8 * N + 6 * L.
16
+ Proof. intros N L. rewrite phys_size by lia. cbn [Nat.pow]. lia. Qed.
17
+
18
  Definition ter (u : su) : Prop := forall x w, In (x, w) (sin u) -> w = 1%Z \/ w = (-1)%Z.
19
 
20
  Lemma ter_lits : forall b ls, ter (SU b (map lw ls)).
 
39
  - destruct r; [exact Hu | apply IH].
40
  Qed.
41
 
42
+ Lemma ter_shapes : forall al N L b j, Forall ter (bshape (nth b (phys_blocks al N L) blk0) j).
43
  Proof.
44
+ intros al N L b j.
45
  do 28 (destruct b as [|b];
46
  [ cbn [nth phys_blocks bshape blk_fld blk_head blk_dec blk_term blk_xs blk_pos blk_neg blk_mt blk_nt
47
  blk_pge blk_b4 blk_bge blk_vand blk_v blk_xhat blk_zero blk_bankh blk_bank blk_desch blk_desc];
 
54
  Qed.
55
 
56
  (* every unit of the netlist reads its predecessors with weight +1 or -1 *)
57
+ Theorem phys_ternary : forall al N L u, In u (phys_units al N L) ->
58
  forall i w, In (i, w) (tin u) -> w = 1%Z \/ w = (-1)%Z.
59
  Proof.
60
+ intros al N L u Hu i w Hin. unfold phys_units, compile in Hu.
61
  apply in_concat in Hu as (us & Hus & Hu). apply in_map_iff in Hus as (blk & <- & Hblk).
62
  unfold flat_block in Hu. apply in_concat in Hu as (us & Hus & Hu).
63
  apply in_map_iff in Hus as (j & <- & Hj). apply in_map_iff in Hu as (v & <- & Hv).
 
65
  cbn [fst snd] in E. injection E as _ <-.
66
  apply In_nth with (d := blk0) in Hblk as (b & _ & <-).
67
  apply In_nth with (d := su0) in Hv as (r & _ & <-).
68
+ exact (ter_nth _ r (ter_shapes al N L b j) x w' Hin).
69
  Qed.
rocq/PhysMain.v CHANGED
@@ -3,89 +3,106 @@ From Stdlib Require Import List Bool Arith ZArith Lia.
3
  Import ListNotations.
4
  From TH Require Import TwoHead Tapes Abstract Net Gates Phys PhysWF PhysSem.
5
 
6
- Lemma F_unfold : forall N L s x,
7
- run (phys_units N L) (nst N L) (fun i => nth i (encode s) false) (idx (nst N L) (phys_blocks N L) x) = F N L s x.
 
8
  Proof. intros. unfold F, X. reflexivity. Qed.
9
 
10
- Lemma wget_clearq_req : forall w a, length w = 64 -> a = 4 \/ a = 5 \/ a = 6 -> wget (clearq w) a = false.
11
  Proof.
12
- intros w a Hw Ha. unfold clearq, AA, AR, AC. rewrite !wget_upd, !upd_length, Hw.
 
 
 
13
  destruct Ha as [-> | [-> | ->]]; reflexivity.
14
  Qed.
15
 
16
  Lemma win_stepres : forall s k qa' ao v, win (stepres s k qa' ao v) = clearq (wnext (win s) ao v).
17
  Proof. intros s k qa' ao v. unfold stepres. cbv zeta. destruct k; reflexivity. Qed.
18
 
19
- Lemma length_wrote : forall s l h, length (wrote s l h) = length l.
20
- Proof. intros s l h. unfold wrote. destruct h; [apply wr0_length|reflexivity]. Qed.
21
 
22
- Lemma length_wrotec : forall s l h, length (wrotec s l h) = length l.
23
- Proof. intros s l h. unfold wrotec. destruct h; [apply wr0_length|reflexivity]. Qed.
24
 
25
- (* From any state whose banks hold N >= 26 bits, whose description tapes hold L >= 1 bits and
26
- whose window has 64 cells, the netlist, evaluated unit by unit on the encoded state, gives
27
- the encoding of the next state. *)
28
- Theorem netlist_step : forall N L s, 26 <= N -> 1 <= L -> length (win s) = 64 ->
29
  length (contents (q0 s)) = N -> length (contents (q1 s)) = N ->
30
  length (contents (d0 s)) = L -> length (contents (d1 s)) = L ->
31
- netstep (nst N L) (phys_units N L) (phys_next N L) (encode s) = encode (step s).
32
  Proof.
33
- intros N L s HN HL Hw H0 H1 H2 H3.
34
- destruct (step_eq N L s HN HL H0 H1 H2 H3 Hw) as (qa' & Hqa & Hst).
35
- pose proof (tapes_step N L s HN HL H0 H1 H2 H3 Hw qa' Hqa) as T. cbv zeta in T.
 
36
  rewrite <- Hst in T. destruct T as (T0 & T1 & T2 & T3).
37
- assert (L0 : length (contents (q0 (step s))) = N)
38
- by (rewrite T0; destruct (kk s), (advA s); rewrite ?rot1_length, ?length_wrote, ?rotn_length; assumption).
39
- assert (L1 : length (contents (q1 (step s))) = N)
40
- by (rewrite T1; destruct (kk s), (advA s); rewrite ?rot1_length, ?length_wrote, ?rotn_length; assumption).
41
- assert (L2 : length (contents (d0 (step s))) = L)
42
- by (rewrite T2; destruct (kk s), (advC s), (advR s); rewrite ?rot1_length, ?length_wrotec; assumption).
43
- assert (L3 : length (contents (d1 (step s))) = L)
44
- by (rewrite T3; destruct (kk s), (advC s), (advR s); rewrite ?rot1_length, ?length_wrotec; assumption).
45
  unfold netstep, phys_next. rewrite map_map.
46
  apply nth_ext with (d := false) (d' := false).
47
- { rewrite length_map, length_seq, (length_encode (step s) N L L0 L1 L2 L3). reflexivity. }
48
  intros i Hi. rewrite length_map, length_seq in Hi.
49
- rewrite (nth_map_seq _ (fun i => run (phys_units N L) (nst N L) (fun i0 => nth i0 (encode s) false)
50
- (idx (nst N L) (phys_blocks N L) (out_ref N L i))) (nst N L) i false Hi).
51
  rewrite F_unfold.
52
- rewrite (nth_encode (step s) N L i L0 L1 L2 L3).
53
  unfold out_ref, oq0, oq1, od0, od1, nst in *.
54
- destruct (Nat.ltb_spec i 61) as [Hw61|Hw61].
55
  - (* the window *)
56
  rewrite Hst, win_stepres.
57
  destruct (Nat.eqb i 1 || Nat.eqb i 2 || Nat.eqb i 3) eqn:E.
58
- + rewrite (F_zero N L s HN HL H0 H1 H2 H3). symmetry. apply wget_clearq_req; [rewrite length_wnext; exact Hw|].
 
59
  unfold realcell. apply orb_true_iff in E as [E|E]; [apply orb_true_iff in E as [E|E]|];
60
  apply Nat.eqb_eq in E; subst i; cbn; lia.
61
- + rewrite (F_xhat N L s HN HL H0 H1 H2 H3 i Hw61).
62
  apply orb_false_iff in E as [E E3]. apply orb_false_iff in E as [E1 E2].
63
  apply Nat.eqb_neq in E1, E2, E3.
64
- assert (Hr : realcell i < 64 /\ realcell i <> 1 /\ realcell i <> 2 /\ realcell i <> 3
65
  /\ realcell i <> AA /\ realcell i <> AR /\ realcell i <> AC)
66
  by (unfold realcell, AA, AR, AC; destruct (Nat.eqb_spec i 0); lia).
67
  destruct Hr as (R0 & R1 & R2 & R3 & R4 & R5 & R6).
68
- rewrite wget_clearq_other by assumption. rewrite (wget_wn N L s) by assumption. reflexivity.
69
- - destruct (Nat.ltb_spec i (61 + N)) as [Hq|Hq].
 
70
  + (* bank 0 *)
71
- rewrite (F_bank N L s HN HL H0 H1 H2 H3 20 21 true 61 (q0 s)
72
- ltac:(left; repeat split) (i - 61) ltac:(lia)).
73
  rewrite T0. destruct (kk s); reflexivity.
74
- + destruct (Nat.ltb_spec i (61 + 2 * N)) as [Hq'|Hq'].
75
  * (* bank 1 *)
76
- replace (i - (61 + N)) with (i - 61 - N) by lia.
77
- rewrite (F_bank N L s HN HL H0 H1 H2 H3 22 23 false (oq1 N) (q1 s)
78
- ltac:(right; repeat split) (i - 61 - N) ltac:(lia)).
79
  rewrite T1. destruct (kk s); reflexivity.
80
- * destruct (Nat.ltb_spec i (61 + 2 * N + L)) as [Hd|Hd].
81
  -- (* description tape 0 *)
82
- replace (i - (61 + 2 * N)) with (i - 61 - 2 * N) by lia.
83
- rewrite (F_desc N L s HN HL H0 H1 H2 H3 24 25 true (od0 N) (d0 s)
84
- ltac:(left; repeat split) (i - 61 - 2 * N) ltac:(lia)).
85
  rewrite T2. destruct (kk s); reflexivity.
86
  -- (* description tape 1 *)
87
- replace (i - (61 + 2 * N + L)) with (i - 61 - 2 * N - L) by lia.
88
- rewrite (F_desc N L s HN HL H0 H1 H2 H3 26 27 false (od1 N L) (d1 s)
89
- ltac:(right; repeat split) (i - 61 - 2 * N - L) ltac:(lia)).
90
  rewrite T3. destruct (kk s); reflexivity.
91
  Qed.
 
 
 
 
 
 
 
 
 
 
 
3
  Import ListNotations.
4
  From TH Require Import TwoHead Tapes Abstract Net Gates Phys PhysWF PhysSem.
5
 
6
+ Lemma F_unfold : forall al N L s x,
7
+ run (phys_units al N L) (nst al N L) (fun i => nth i (encode al s) false) (idx (nst al N L) (phys_blocks al N L) x)
8
+ = F al N L s x.
9
  Proof. intros. unfold F, X. reflexivity. Qed.
10
 
11
+ Lemma wget_clearq_req : forall w a, 7 <= length w -> a = 4 \/ a = 5 \/ a = 6 -> wget (clearq w) a = false.
12
  Proof.
13
+ intros w a Hw Ha. unfold clearq, AA, AR, AC. rewrite !wget_upd, !upd_length.
14
+ replace (Nat.ltb 4 (length w)) with true by (symmetry; apply Nat.ltb_lt; lia).
15
+ replace (Nat.ltb 5 (length w)) with true by (symmetry; apply Nat.ltb_lt; lia).
16
+ replace (Nat.ltb 6 (length w)) with true by (symmetry; apply Nat.ltb_lt; lia).
17
  destruct Ha as [-> | [-> | ->]]; reflexivity.
18
  Qed.
19
 
20
  Lemma win_stepres : forall s k qa' ao v, win (stepres s k qa' ao v) = clearq (wnext (win s) ao v).
21
  Proof. intros s k qa' ao v. unfold stepres. cbv zeta. destruct k; reflexivity. Qed.
22
 
23
+ Lemma length_wrote : forall al s l h, length (wrote al s l h) = length l.
24
+ Proof. intros al s l h. unfold wrote. destruct h; [apply wr0_length|reflexivity]. Qed.
25
 
26
+ Lemma length_wrotec : forall al s l h, length (wrotec al s l h) = length l.
27
+ Proof. intros al s l h. unfold wrotec. destruct h; [apply wr0_length|reflexivity]. Qed.
28
 
29
+ (* From any state of the machine with a window of 2^al cells, al >= 3, whose banks hold
30
+ N >= 8 + 3 al bits and whose description tapes hold L >= 1 bits, the netlist, evaluated unit
31
+ by unit on the encoded state, gives the encoding of the next state. *)
32
+ Theorem netlist_step : forall al N L s, 3 <= al -> recw al <= N -> 1 <= L -> length (win s) = W al ->
33
  length (contents (q0 s)) = N -> length (contents (q1 s)) = N ->
34
  length (contents (d0 s)) = L -> length (contents (d1 s)) = L ->
35
+ netstep (nst al N L) (phys_units al N L) (phys_next al N L) (encode al s) = encode al (stepA al s).
36
  Proof.
37
+ intros al N L s Hal HN HL Hw H0 H1 H2 H3.
38
+ pose proof (W_ge al Hal) as HW8. pose proof (nwin_W al Hal) as HnW.
39
+ destruct (step_eq al N L s Hal HN HL H0 H1 H2 H3 Hw) as (qa' & Hqa & Hst).
40
+ pose proof (tapes_step al N L s Hal HN HL H0 H1 H2 H3 Hw qa' Hqa) as T. cbv zeta in T.
41
  rewrite <- Hst in T. destruct T as (T0 & T1 & T2 & T3).
42
+ assert (L0 : length (contents (q0 (stepA al s))) = N)
43
+ by (rewrite T0; destruct (kk s), (advA al s); rewrite ?rot1_length, ?length_wrote, ?rotn_length; assumption).
44
+ assert (L1 : length (contents (q1 (stepA al s))) = N)
45
+ by (rewrite T1; destruct (kk s), (advA al s); rewrite ?rot1_length, ?length_wrote, ?rotn_length; assumption).
46
+ assert (L2 : length (contents (d0 (stepA al s))) = L)
47
+ by (rewrite T2; destruct (kk s), (advC al s), (advR al s); rewrite ?rot1_length, ?length_wrotec; assumption).
48
+ assert (L3 : length (contents (d1 (stepA al s))) = L)
49
+ by (rewrite T3; destruct (kk s), (advC al s), (advR al s); rewrite ?rot1_length, ?length_wrotec; assumption).
50
  unfold netstep, phys_next. rewrite map_map.
51
  apply nth_ext with (d := false) (d' := false).
52
+ { rewrite length_map, length_seq, (length_encode al (stepA al s) N L L0 L1 L2 L3). reflexivity. }
53
  intros i Hi. rewrite length_map, length_seq in Hi.
54
+ rewrite (nth_map_seq _ (fun i => run (phys_units al N L) (nst al N L) (fun i0 => nth i0 (encode al s) false)
55
+ (idx (nst al N L) (phys_blocks al N L) (out_ref al N L i))) (nst al N L) i false Hi).
56
  rewrite F_unfold.
57
+ rewrite (nth_encode al (stepA al s) N L i L0 L1 L2 L3).
58
  unfold out_ref, oq0, oq1, od0, od1, nst in *.
59
+ destruct (Nat.ltb_spec i (nwin al)) as [Hwi|Hwi].
60
  - (* the window *)
61
  rewrite Hst, win_stepres.
62
  destruct (Nat.eqb i 1 || Nat.eqb i 2 || Nat.eqb i 3) eqn:E.
63
+ + rewrite (F_zero al N L s Hal HN HL H0 H1 H2 H3). symmetry.
64
+ apply wget_clearq_req; [rewrite length_wnext; lia|].
65
  unfold realcell. apply orb_true_iff in E as [E|E]; [apply orb_true_iff in E as [E|E]|];
66
  apply Nat.eqb_eq in E; subst i; cbn; lia.
67
+ + rewrite (F_xhat al N L s Hal HN HL H0 H1 H2 H3 i Hwi).
68
  apply orb_false_iff in E as [E E3]. apply orb_false_iff in E as [E1 E2].
69
  apply Nat.eqb_neq in E1, E2, E3.
70
+ assert (Hr : realcell i < W al /\ realcell i <> 1 /\ realcell i <> 2 /\ realcell i <> 3
71
  /\ realcell i <> AA /\ realcell i <> AR /\ realcell i <> AC)
72
  by (unfold realcell, AA, AR, AC; destruct (Nat.eqb_spec i 0); lia).
73
  destruct Hr as (R0 & R1 & R2 & R3 & R4 & R5 & R6).
74
+ rewrite wget_clearq_other by assumption.
75
+ rewrite (wget_wn al N L s Hal HN HL H0 H1 H2 H3 Hw) by assumption. reflexivity.
76
+ - destruct (Nat.ltb_spec i (nwin al + N)) as [Hq|Hq].
77
  + (* bank 0 *)
78
+ rewrite (F_bank al N L s Hal HN HL H0 H1 H2 H3 20 21 true (oq0 al) (q0 s)
79
+ ltac:(left; repeat split) (i - nwin al) ltac:(lia)).
80
  rewrite T0. destruct (kk s); reflexivity.
81
+ + destruct (Nat.ltb_spec i (nwin al + 2 * N)) as [Hq'|Hq'].
82
  * (* bank 1 *)
83
+ replace (i - (nwin al + N)) with (i - nwin al - N) by lia.
84
+ rewrite (F_bank al N L s Hal HN HL H0 H1 H2 H3 22 23 false (oq1 al N) (q1 s)
85
+ ltac:(right; repeat split) (i - nwin al - N) ltac:(lia)).
86
  rewrite T1. destruct (kk s); reflexivity.
87
+ * destruct (Nat.ltb_spec i (nwin al + 2 * N + L)) as [Hd|Hd].
88
  -- (* description tape 0 *)
89
+ replace (i - (nwin al + 2 * N)) with (i - nwin al - 2 * N) by lia.
90
+ rewrite (F_desc al N L s Hal HN HL H0 H1 H2 H3 24 25 true (od0 al N) (d0 s)
91
+ ltac:(left; repeat split) (i - nwin al - 2 * N) ltac:(lia)).
92
  rewrite T2. destruct (kk s); reflexivity.
93
  -- (* description tape 1 *)
94
+ replace (i - (nwin al + 2 * N + L)) with (i - nwin al - 2 * N - L) by lia.
95
+ rewrite (F_desc al N L s Hal HN HL H0 H1 H2 H3 26 27 false (od1 al N L) (d1 s)
96
+ ltac:(right; repeat split) (i - nwin al - 2 * N - L) ltac:(lia)).
97
  rewrite T3. destruct (kk s); reflexivity.
98
  Qed.
99
+
100
+ (* the machine of 64 cells, which runs the organism, is the case al = 6 *)
101
+ Corollary netlist_step64 : forall N L s, 26 <= N -> 1 <= L -> length (win s) = 64 ->
102
+ length (contents (q0 s)) = N -> length (contents (q1 s)) = N ->
103
+ length (contents (d0 s)) = L -> length (contents (d1 s)) = L ->
104
+ netstep (nst 6 N L) (phys_units 6 N L) (phys_next 6 N L) (encode 6 s) = encode 6 (step s).
105
+ Proof.
106
+ intros N L s HN HL Hw H0 H1 H2 H3. rewrite <- stepA_6.
107
+ apply netlist_step; first [exact Hw | assumption | unfold recw; lia | lia].
108
+ Qed.
rocq/PhysSem.v CHANGED
@@ -4,39 +4,124 @@ Import ListNotations.
4
  From TH Require Import TwoHead Tapes Abstract Net Gates Phys PhysWF.
5
 
6
  (* the state bits of a machine state, in the order of the netlist *)
7
- Definition encode (s : st) : list bool :=
8
- map (fun j => wget (win s) (realcell j)) (seq 0 61)
9
  ++ contents (q0 s) ++ contents (q1 s) ++ contents (d0 s) ++ contents (d1 s).
10
 
11
- Lemma length_encode : forall s N L,
12
  length (contents (q0 s)) = N -> length (contents (q1 s)) = N ->
13
  length (contents (d0 s)) = L -> length (contents (d1 s)) = L ->
14
- length (encode s) = nst N L.
15
  Proof.
16
- intros s N L H1 H2 H3 H4. unfold encode, nst.
17
  rewrite !length_app, length_map, length_seq, H1, H2, H3, H4. lia.
18
  Qed.
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  Section Sem.
21
- Variables (N L : nat) (s : st).
22
- Hypotheses (HN : 26 <= N) (HL : 1 <= L)
23
  (Hq0 : length (contents (q0 s)) = N) (Hq1 : length (contents (q1 s)) = N)
24
  (Hd0 : length (contents (d0 s)) = L) (Hd1 : length (contents (d1 s)) = L).
25
 
26
- Definition X (i : nat) : bool := nth i (encode s) false.
27
- Definition F (x : sref) : bool := run (phys_units N L) (nst N L) X (idx (nst N L) (phys_blocks N L) x).
 
 
 
 
28
 
29
- Lemma X_win : forall j, j < 61 -> X j = wget (win s) (realcell j).
30
  Proof.
31
  intros j Hj. unfold X, encode. rewrite app_nth1 by (rewrite length_map, length_seq; exact Hj).
32
- exact (nth_map_seq _ (fun j => wget (win s) (realcell j)) 61 j false Hj).
33
  Qed.
34
 
35
  Lemma X_tape : forall i,
36
- (i < N -> X (oq0 + i) = nth i (contents (q0 s)) false)
37
- /\ (i < N -> X (oq1 N + i) = nth i (contents (q1 s)) false)
38
- /\ (i < L -> X (od0 N + i) = nth i (contents (d0 s)) false)
39
- /\ (i < L -> X (od1 N L + i) = nth i (contents (d1 s)) false).
40
  Proof.
41
  intro i. unfold X, encode, oq0, oq1, od0, od1.
42
  repeat split; intro Hi; rewrite app_nth2 by (rewrite length_map, length_seq; lia);
@@ -47,32 +132,32 @@ Proof.
47
  - rewrite app_nth2 by lia. rewrite app_nth2 by lia. rewrite app_nth2 by lia. f_equal. lia.
48
  Qed.
49
 
50
- Lemma F_in : forall i, i < nst N L -> F (SIn i) = X i.
51
  Proof. intros i H. unfold F, phys_units. apply net_in. exact H. Qed.
52
 
53
  Lemma F_unit : forall b j r, b < 28 ->
54
- j < bcount (nth b (phys_blocks N L) blk0) -> r < bsize (nth b (phys_blocks N L) blk0) ->
55
- F (SRef b j r) = sfire' (sunit (phys_blocks N L) b j r) F.
56
  Proof.
57
  intros b j r Hb Hj Hr. unfold F, phys_units. apply net_unit; [apply phys_wf; assumption| |assumption|assumption].
58
  rewrite phys_length. exact Hb.
59
  Qed.
60
 
61
  Lemma F_and : forall b j r ls, b < 28 ->
62
- j < bcount (nth b (phys_blocks N L) blk0) -> r < bsize (nth b (phys_blocks N L) blk0) ->
63
- sunit (phys_blocks N L) b j r = sAND ls -> F (SRef b j r) = forallb (litv F) ls.
64
  Proof. intros b j r ls Hb Hj Hr E. rewrite F_unit, E by assumption. apply sAND_fire. Qed.
65
 
66
  Lemma F_or : forall b j r ls, b < 28 ->
67
- j < bcount (nth b (phys_blocks N L) blk0) -> r < bsize (nth b (phys_blocks N L) blk0) ->
68
- sunit (phys_blocks N L) b j r = sOR ls -> F (SRef b j r) = existsb (litv F) ls.
69
  Proof. intros b j r ls Hb Hj Hr E. rewrite F_unit, E by assumption. apply sOR_fire. Qed.
70
 
71
  Lemma F_mux : forall b j r0 sel a c, b < 28 ->
72
- j < bcount (nth b (phys_blocks N L) blk0) -> S (S r0) < bsize (nth b (phys_blocks N L) blk0) ->
73
- sunit (phys_blocks N L) b j r0 = sAND [(sel, true); (a, true)] ->
74
- sunit (phys_blocks N L) b j (S r0) = sAND [(sel, false); (c, true)] ->
75
- sunit (phys_blocks N L) b j (S (S r0)) = sOR [(SRef b j r0, true); (SRef b j (S r0), true)] ->
76
  F (SRef b j (S (S r0))) = if F sel then F a else F c.
77
  Proof.
78
  intros b j r0 sel a c Hb Hj Hr E0 E1 E2.
@@ -93,10 +178,10 @@ Ltac blk_compute := cbn [nth phys_blocks bcount bsize bshape blk_fld blk_head bl
93
  blk_desch blk_desc sunit mux3 app].
94
 
95
  (* block 0: the record in front of the active bank *)
96
- Lemma F_fld : forall j, j < 26 -> F (fld j) = nth j qa false.
97
  Proof.
98
  intros j Hj. unfold fld.
99
- rewrite (F_mux 0 j 0 kap (SIn (oq1 N + j)) (SIn (oq0 + j))) by (blk_compute; first [lia | reflexivity]).
100
  rewrite F_kap, !F_in by (unfold nst, oq0, oq1; lia).
101
  destruct (X_tape j) as (T0 & T1 & _ & _). rewrite T0, T1 by lia.
102
  unfold qa. destruct kk; reflexivity.
@@ -116,47 +201,22 @@ Proof.
116
  rewrite <- !plus_n_O in T0, T1, T2, T3.
117
  repeat split;
118
  (rewrite F_mux with (sel := kap)
119
- (a := SIn (fst (head_src N L _))) (c := SIn (snd (head_src N L _))) by (blk_compute; first [lia | reflexivity]));
120
  cbn [head_src fst snd]; rewrite F_kap, !F_in by (unfold nst, oq0, oq1, od0, od1; lia);
121
  rewrite ?T0, ?T1, ?T2, ?T3 by lia; unfold rdv, armv, cpyv; rewrite <- ?nth0_hd0; destruct kk; reflexivity.
122
  Qed.
123
 
124
  (* ---- blocks 2 to 8: the three addresses and the two operands --------------------- *)
125
- Lemma bits2nat_nat2bits : forall a, a < 64 -> bits2nat (nat2bits 6 a) = a.
126
- Proof. intros a Ha. do 64 (destruct a as [|a]; [reflexivity|]). lia. Qed.
127
-
128
- Lemma nat2bits_bits2nat : forall f, length f = 6 -> nat2bits 6 (bits2nat f) = f.
129
- Proof.
130
- intros f Hf. destruct f as [|b0 [|b1 [|b2 [|b3 [|b4 [|b5 [|? ?]]]]]]]; cbn [length] in Hf; try lia.
131
- destruct b0, b1, b2, b3, b4, b5; reflexivity.
132
- Qed.
133
-
134
- Lemma lbeq_refl : forall l, lbeq l l = true.
135
- Proof. induction l as [|x l IH]; [reflexivity|]. cbn [lbeq]. rewrite IH, eqb_reflx. reflexivity. Qed.
136
-
137
- Lemma dec_ok : forall f a, length f = 6 -> a < 64 ->
138
- forallb (fun t => Bool.eqb (nth t f false) (bitsof a t)) (seq 0 6) = Nat.eqb (bits2nat f) a.
139
- Proof.
140
- intros f a Hf Ha.
141
- assert (E : forallb (fun t => Bool.eqb (nth t f false) (bitsof a t)) (seq 0 6) = lbeq f (nat2bits 6 a)).
142
- { destruct f as [|b0 [|b1 [|b2 [|b3 [|b4 [|b5 [|? ?]]]]]]]; cbn [length] in Hf; try lia.
143
- unfold bitsof. cbn - [Nat.testbit Bool.eqb]. rewrite !andb_true_r. reflexivity. }
144
- rewrite E. destruct (lbeq f (nat2bits 6 a)) eqn:El; destruct (Nat.eqb (bits2nat f) a) eqn:En; auto.
145
- - apply lbeq_true in El. subst f. rewrite bits2nat_nat2bits in En by exact Ha.
146
- rewrite Nat.eqb_refl in En. discriminate.
147
- - apply Nat.eqb_eq in En. rewrite <- En, nat2bits_bits2nat, lbeq_refl in El by exact Hf. discriminate.
148
- Qed.
149
-
150
  Lemma length_qa : length qa = N.
151
  Proof. unfold qa. destruct kk; assumption. Qed.
152
 
153
- Definition field (o : nat) : list bool := firstn 6 (skipn o qa).
154
 
155
- Lemma length_field : forall o, o + 6 <= 26 -> length (field o) = 6.
156
  Proof. intros o Ho. unfold field. rewrite length_firstn, length_skipn, length_qa. lia. Qed.
157
 
158
- Lemma nth_field : forall o t, t < 6 -> nth t (field o) false = nth (o + t) qa false.
159
- Proof. intros o t Ht. unfold field. rewrite nth_firstn. destruct (Nat.ltb_spec t 6); [|lia]. apply nth_skipn. Qed.
160
 
161
  Lemma forallb_map' : forall (A B : Type) (f : B -> bool) (g : A -> B) l, forallb f (map g l) = forallb (fun x => f (g x)) l.
162
  Proof. intros A B f g l. induction l as [|x l IH]; [reflexivity|]. cbn [map forallb]. rewrite IH. reflexivity. Qed.
@@ -170,15 +230,15 @@ Proof. intros A f g l H. induction l as [|x l IH]; [reflexivity|]. cbn [forallb]
170
  Lemma existsb_ext_in : forall (A : Type) (f g : A -> bool) l, (forall x, In x l -> f x = g x) -> existsb f l = existsb g l.
171
  Proof. intros A f g l H. induction l as [|x l IH]; [reflexivity|]. cbn [existsb]. rewrite H, IH; [reflexivity| |left; reflexivity]. intros y Hy. apply H. right. exact Hy. Qed.
172
 
173
- Lemma F_dec : forall b o a, (b = 2 /\ o = 8) \/ (b = 3 /\ o = 14) \/ (b = 4 /\ o = 20) -> a < 64 ->
174
  F (SRef b a 0) = Nat.eqb (bits2nat (field o)) a.
175
  Proof.
176
  intros b o a Hbo Ha.
177
- assert (Hs : sunit (phys_blocks N L) b a 0 = sAND (map (fun t => (fld (o + t), bitsof a t)) (seq 0 6)))
178
  by (destruct Hbo as [[-> ->]|[[-> ->]|[-> ->]]]; reflexivity).
179
  rewrite (F_and b a 0 _ ltac:(lia) ltac:(destruct Hbo as [[-> ->]|[[-> ->]|[-> ->]]]; blk_compute; lia)
180
  ltac:(destruct Hbo as [[-> ->]|[[-> ->]|[-> ->]]]; blk_compute; lia) Hs).
181
- rewrite forallb_map'. rewrite <- dec_ok by (try apply length_field; lia).
182
  apply forallb_ext_in. intros t Ht. apply in_seq in Ht.
183
  unfold litv. cbn [fst snd]. rewrite F_fld by lia. rewrite nth_field by lia. reflexivity.
184
  Qed.
@@ -186,7 +246,7 @@ Qed.
186
  Definition cellv (a : nat) : bool :=
187
  if Nat.eqb a 1 then rdv else if Nat.eqb a 2 then armv else if Nat.eqb a 3 then cpyv else wget (win s) a.
188
 
189
- Lemma F_cellref : forall a, a < 64 -> F (cellref a) = cellv a.
190
  Proof.
191
  intros a Ha. destruct F_heads as (H1 & H2 & H3). unfold cellref, cellv.
192
  destruct (Nat.eqb_spec a 1) as [->|E1]; [exact H1|].
@@ -215,43 +275,41 @@ Qed.
215
 
216
  Definition fa (o : nat) : nat := bits2nat (field o).
217
 
218
- Lemma fa_lt : forall o, o + 6 <= 26 -> fa o < 64.
219
  Proof.
220
- intros o Ho. unfold fa. pose proof (length_field o Ho) as H.
221
- rewrite <- (nat2bits_bits2nat (field o) H). remember (field o) as f eqn:Ef. clear Ef.
222
- destruct f as [|b0 [|b1 [|b2 [|b3 [|b4 [|b5 [|? ?]]]]]]]; cbn [length] in H; try lia.
223
- destruct b0, b1, b2, b3, b4, b5; cbn; lia.
224
  Qed.
225
 
226
- Lemma F_xs : forall i, i < 2 -> F (xsref i) = cellv (fa (8 + 6 * i)).
227
  Proof.
228
  intros i Hi. unfold xsref.
229
- assert (Hlt := fa_lt (8 + 6 * i) ltac:(lia)).
230
  destruct (Nat.eqb_spec i 0) as [->|E].
231
- - rewrite (F_or 6 0 0 (map (fun a => (SRef 5 a 0, true)) (seq 0 64))) by (blk_compute; first [lia | reflexivity]).
 
232
  rewrite existsb_map'.
233
  rewrite (existsb_ext_in _ _ (fun a => Nat.eqb (fa 8) a && cellv a)).
234
- + rewrite existsb_pick. cbn [Nat.mul Nat.add] in Hlt.
235
  replace (0 <=? fa 8) with true by (symmetry; apply Nat.leb_le; lia).
236
- replace (fa 8 <? 0 + 64) with true by (symmetry; apply Nat.ltb_lt; lia).
237
  reflexivity.
238
  + intros a Ha. apply in_seq in Ha. rewrite litv_pos.
239
  rewrite (F_and 5 a 0 [(SRef 2 a 0, true); (cellref a, true)]) by (blk_compute; first [lia | reflexivity]).
240
  cbn [forallb]. rewrite !litv_pos, andb_true_r.
241
  rewrite F_dec with (o := 8) by (lia || (left; split; reflexivity)).
242
  rewrite F_cellref by lia. reflexivity.
243
- - replace i with 1 in * by lia.
244
- rewrite (F_or 8 0 0 (map (fun a => (SRef 7 a 0, true)) (seq 0 64))) by (blk_compute; first [lia | reflexivity]).
 
245
  rewrite existsb_map'.
246
- rewrite (existsb_ext_in _ _ (fun a => Nat.eqb (fa 14) a && cellv a)).
247
- + rewrite existsb_pick. cbn [Nat.mul Nat.add] in Hlt.
248
- replace (0 <=? fa 14) with true by (symmetry; apply Nat.leb_le; lia).
249
- replace (fa 14 <? 0 + 64) with true by (symmetry; apply Nat.ltb_lt; lia).
250
  reflexivity.
251
  + intros a Ha. apply in_seq in Ha. rewrite litv_pos.
252
  rewrite (F_and 7 a 0 [(SRef 3 a 0, true); (cellref a, true)]) by (blk_compute; first [lia | reflexivity]).
253
  cbn [forallb]. rewrite !litv_pos, andb_true_r.
254
- rewrite F_dec with (o := 14) by (lia || (right; left; split; reflexivity)).
255
  rewrite F_cellref by lia. reflexivity.
256
  Qed.
257
 
@@ -275,7 +333,7 @@ Lemma vnet_ok : forall f0 f1 f2 f3 f4 f5 f6 f7 y0 y1,
275
  = Z.leb 0 (wcode f0 f1 * b2z y0 + wcode f2 f3 * b2z y1 + beta4 f4 f5 f6 f7)%Z.
276
  Proof. intros. destruct f0, f1, f2, f3, f4, f5, f6, f7, y0, y1; reflexivity. Qed.
277
 
278
- Lemma F_fb : forall j, j < 26 -> F (fld j) = fb j.
279
  Proof. exact F_fld. Qed.
280
 
281
  Lemma F_v : F (SRef 17 0 0) =
@@ -331,19 +389,22 @@ Proof.
331
  Qed.
332
 
333
  (* ---- blocks 18 and 19: the window after the write --------------------------------- *)
334
- Definition vsem : bool := vnet (fb 0) (fb 1) (fb 2) (fb 3) (fb 4) (fb 5) (fb 6) (fb 7) (cellv (fa 8)) (cellv (fa 14)).
335
- Definition aosem : nat := fa 20.
 
336
 
337
  Lemma F_vsem : F (SRef 17 0 0) = vsem.
338
- Proof. rewrite F_v, (F_xs 0), (F_xs 1) by lia. reflexivity. Qed.
 
 
339
 
340
- Lemma F_lo : forall a, a < 64 -> F (SRef 4 a 0) = Nat.eqb aosem a.
341
  Proof. intros a Ha. apply F_dec; [right; right; split; reflexivity | exact Ha]. Qed.
342
 
343
- Lemma realcell_lt : forall j, j < 61 -> realcell j < 64.
344
  Proof. intros j Hj. unfold realcell. destruct (Nat.eqb j 0); lia. Qed.
345
 
346
- Lemma F_xhat : forall j, j < 61 ->
347
  F (SRef 18 j 2) = if Nat.eqb aosem (realcell j) then vsem else wget (win s) (realcell j).
348
  Proof.
349
  intros j Hj.
@@ -392,30 +453,30 @@ Proof.
392
  destruct here; destruct m; reflexivity.
393
  Qed.
394
 
395
- Lemma F_bank : forall bh bb idlep base T, (bh = 20 /\ bb = 21 /\ idlep = true /\ base = oq0 /\ T = q0 s)
396
- \/ (bh = 22 /\ bb = 23 /\ idlep = false /\ base = oq1 N /\ T = q1 s) ->
397
  forall i, i < N ->
398
  F (SRef bb i 3) =
399
  if Bool.eqb kk idlep
400
  then nth i (if advA then rot1 (wrote (contents T) (Nat.eqb aosem 2)) else wrote (contents T) (Nat.eqb aosem 2)) false
401
- else nth i (rotn 26 (contents T)) false.
402
  Proof.
403
  intros bh bb idlep base T Hsel i Hi.
404
  assert (HT : length (contents T) = N) by (destruct Hsel as [(_ & _ & _ & _ & ->)|(_ & _ & _ & _ & ->)]; assumption).
405
  assert (Hx : forall m, m < N -> X (base + m) = nth m (contents T) false).
406
  { intros m Hm. destruct (X_tape m) as (T0 & T1 & _ & _).
407
  destruct Hsel as [(_ & _ & _ & -> & ->)|(_ & _ & _ & -> & ->)]; [apply T0|apply T1]; exact Hm. }
408
- assert (Hb : base + N <= nst N L) by (destruct Hsel as [(_ & _ & _ & -> & _)|(_ & _ & _ & -> & _)]; unfold nst, oq0, oq1; lia).
409
- assert (Hc : forall j r, j < N -> r < 4 -> bb < 28 /\ j < bcount (nth bb (phys_blocks N L) blk0)
410
- /\ r < bsize (nth bb (phys_blocks N L) blk0))
411
  by (intros; destruct Hsel as [(_ & -> & _)|(_ & -> & _)]; blk_compute; lia).
412
- assert (Hh : forall r, r < 6 -> bh < 28 /\ 0 < bcount (nth bh (phys_blocks N L) blk0)
413
- /\ r < bsize (nth bh (phys_blocks N L) blk0))
414
  by (intros; destruct Hsel as [(-> & _)|(-> & _)]; blk_compute; lia).
415
- assert (Hsu : forall j, sunit (phys_blocks N L) bb j 3 = sOR [(SRef bb j 0, true); (SRef bb j 1, true); (SRef bb j 2, true)]
416
- /\ sunit (phys_blocks N L) bb j 0 = sAND [(kap, negb idlep); (SIn (base + (j + 26) mod N), true)]
417
- /\ sunit (phys_blocks N L) bb j 1 = sAND [(SRef bh 0 0, true); (hatq bh base ((j + 1) mod N), true)]
418
- /\ sunit (phys_blocks N L) bb j 2 = sAND [(SRef bh 0 1, true); (hatq bh base j, true)])
419
  by (intro j; destruct Hsel as [(-> & -> & -> & -> & _)|(-> & -> & -> & -> & _)]; repeat split; reflexivity).
420
  destruct (Hsu i) as (S3 & S0 & S1 & S2).
421
  destruct (Hc i 3 Hi ltac:(lia)) as (C1 & C2 & C3).
@@ -430,12 +491,12 @@ Proof.
430
  { destruct F_adv as (_ & HA & _).
431
  destruct (Hh 0 ltac:(lia)) as (H01 & H02 & H03). destruct (Hh 1 ltac:(lia)) as (H11 & H12 & H13).
432
  destruct (Hh 2 ltac:(lia)) as (H21 & H22 & H23). destruct (Hh 5 ltac:(lia)) as (H51 & H52 & H53).
433
- assert (U : sunit (phys_blocks N L) bh 0 0 = sAND [(kap, idlep); (SRef 18 2 2, true)]
434
- /\ sunit (phys_blocks N L) bh 0 1 = sAND [(kap, idlep); (SRef 18 2 2, false)]
435
- /\ sunit (phys_blocks N L) bh 0 2 = sAND [(SRef 4 2 0, true); (kap, idlep)]
436
- /\ sunit (phys_blocks N L) bh 0 3 = sAND [(SRef bh 0 2, true); (SRef 17 0 0, true)]
437
- /\ sunit (phys_blocks N L) bh 0 4 = sAND [(SRef bh 0 2, false); (SIn base, true)]
438
- /\ sunit (phys_blocks N L) bh 0 5 = sOR [(SRef bh 0 3, true); (SRef bh 0 4, true)])
439
  by (destruct Hsel as [(-> & -> & -> & -> & _)|(-> & -> & -> & -> & _)]; repeat split; reflexivity).
440
  destruct U as (U0 & U1 & U2 & U3 & U4 & U5).
441
  split; [|split].
@@ -454,7 +515,7 @@ Proof.
454
  { intros m Hm. rewrite nth_wrote by lia. unfold hatq. destruct (Nat.eqb_spec m 0) as [->|E].
455
  - rewrite Hh0. reflexivity.
456
  - rewrite F_in by lia. apply Hx. exact Hm. }
457
- pose proof (Nat.mod_upper_bound (i + 1) N ltac:(lia)). pose proof (Nat.mod_upper_bound (i + 26) N ltac:(lia)).
458
  rewrite Hia, Hih, !Hhat by lia. unfold litv. cbn [fst snd]. rewrite F_kap, F_in by lia. rewrite Hx by lia.
459
  destruct (Bool.eqb kk idlep) eqn:Ek.
460
  - apply Bool.eqb_prop in Ek. subst idlep. rewrite ?eqb_reflx, ?andb_true_r.
@@ -472,8 +533,8 @@ Qed.
472
  (* ---- blocks 24 to 27: the description tapes -------------------------------------- *)
473
  Definition wrotec (l : list bool) (here : bool) : list bool := if here then wr0 l vsem else l.
474
 
475
- Lemma F_desc : forall dh db idlep base T, (dh = 24 /\ db = 25 /\ idlep = true /\ base = od0 N /\ T = d0 s)
476
- \/ (dh = 26 /\ db = 27 /\ idlep = false /\ base = od1 N L /\ T = d1 s) ->
477
  forall i, i < L ->
478
  F (SRef db i 2) =
479
  if Bool.eqb kk idlep
@@ -485,18 +546,18 @@ Proof.
485
  assert (Hx : forall m, m < L -> X (base + m) = nth m (contents T) false).
486
  { intros m Hm. destruct (X_tape m) as (_ & _ & T2 & T3).
487
  destruct Hsel as [(_ & _ & _ & -> & ->)|(_ & _ & _ & -> & ->)]; [apply T2|apply T3]; exact Hm. }
488
- assert (Hb : base + L <= nst N L)
489
  by (destruct Hsel as [(_ & _ & _ & -> & _)|(_ & _ & _ & -> & _)]; unfold nst, od0, od1; lia).
490
- assert (Hh : forall r, r < 7 -> dh < 28 /\ 0 < bcount (nth dh (phys_blocks N L) blk0)
491
- /\ r < bsize (nth dh (phys_blocks N L) blk0))
492
  by (intros; destruct Hsel as [(-> & _)|(-> & _)]; blk_compute; lia).
493
- assert (U : sunit (phys_blocks N L) dh 0 0 = sAND [(kap, negb idlep); (SRef 18 1 2, true)]
494
- /\ sunit (phys_blocks N L) dh 0 1 = sAND [(kap, idlep); (SRef 18 3 2, true)]
495
- /\ sunit (phys_blocks N L) dh 0 2 = sOR [(SRef dh 0 0, true); (SRef dh 0 1, true)]
496
- /\ sunit (phys_blocks N L) dh 0 3 = sAND [(SRef 4 3 0, true); (kap, idlep)]
497
- /\ sunit (phys_blocks N L) dh 0 4 = sAND [(SRef dh 0 3, true); (SRef 17 0 0, true)]
498
- /\ sunit (phys_blocks N L) dh 0 5 = sAND [(SRef dh 0 3, false); (SIn base, true)]
499
- /\ sunit (phys_blocks N L) dh 0 6 = sOR [(SRef dh 0 4, true); (SRef dh 0 5, true)])
500
  by (destruct Hsel as [(-> & -> & -> & -> & _)|(-> & -> & -> & -> & _)]; repeat split; reflexivity).
501
  destruct U as (U0 & U1 & U2 & U3 & U4 & U5 & U6).
502
  destruct F_adv as (HR & _ & HC).
@@ -522,12 +583,12 @@ Proof.
522
  - rewrite Hh0. destruct (Nat.eqb aosem 3 && Bool.eqb kk idlep); reflexivity.
523
  - rewrite F_in by lia. rewrite Hx by exact Hm.
524
  destruct (Nat.eqb aosem 3 && Bool.eqb kk idlep); [destruct m; [lia|]|]; reflexivity. }
525
- assert (Hm3 : sunit (phys_blocks N L) db i 0 = sAND [(SRef dh 0 2, true); (hatd dh base ((i + 1) mod L), true)]
526
- /\ sunit (phys_blocks N L) db i 1 = sAND [(SRef dh 0 2, false); (hatd dh base i, true)]
527
- /\ sunit (phys_blocks N L) db i 2 = sOR [(SRef db i 0, true); (SRef db i 1, true)])
528
  by (destruct Hsel as [(-> & -> & _ & -> & _)|(-> & -> & _ & -> & _)]; repeat split; reflexivity).
529
  destruct Hm3 as (M0 & M1 & M2).
530
- assert (Hc : db < 28 /\ i < bcount (nth db (phys_blocks N L) blk0) /\ 2 < bsize (nth db (phys_blocks N L) blk0))
531
  by (destruct Hsel as [(_ & -> & _)|(_ & -> & _)]; blk_compute; lia).
532
  destruct Hc as (C1 & C2 & C3).
533
  rewrite (F_mux db i 0 (SRef dh 0 2) (hatd dh base ((i + 1) mod L)) (hatd dh base i) C1 C2 C3 M0 M1 M2).
@@ -541,7 +602,7 @@ Proof.
541
  Qed.
542
 
543
  (* ---- one step of the machine in the same terms ------------------------------------ *)
544
- Hypothesis Hw : length (win s) = 64.
545
 
546
  Lemma firstn_skipn_firstn : forall (l : list bool) a b c, a + b <= c ->
547
  firstn b (skipn a (firstn c l)) = firstn b (skipn a l).
@@ -554,35 +615,38 @@ Proof.
554
  reflexivity.
555
  Qed.
556
 
557
- Lemma decode_qa : decode (firstn 26 qa) =
558
- Rec (fa 8) (wcode (fb 0) (fb 1)) (fa 14) (wcode (fb 2) (fb 3)) (beta4 (fb 4) (fb 5) (fb 6) (fb 7)) (fa 20).
 
559
  Proof.
560
- unfold decode. cbv zeta. unfold fa, field, fb, beta4.
561
- rewrite !firstn_skipn_firstn by lia. rewrite !nth_firstn. cbn [Nat.ltb Nat.leb].
562
- rewrite firstn4_skipn4 by (rewrite length_qa; lia). reflexivity.
 
 
563
  Qed.
564
 
565
  Lemma cellof_cellv : forall a, cellof s kk a = cellv a.
566
  Proof. intro a. unfold cellof, cellv, rdv, armv, cpyv. rewrite !hd_t_contents. reflexivity. Qed.
567
 
568
- Lemma step_eq : exists qa', contents qa' = rotn 26 qa /\ step s = stepres s kk qa' aosem vsem.
569
  Proof.
570
- destruct (popn 26 (if kk then q1 s else q0 s)) as [bits qa'] eqn:Ep.
571
  exists qa'. split.
572
- - pose proof (popn_turns 26 (if kk then q1 s else q0 s)) as P. rewrite Ep in P. exact P.
573
- - assert (Hbits : bits = firstn 26 qa).
574
- { pose proof (popn_reads 26 (if kk then q1 s else q0 s)) as P. rewrite Ep in P. apply P.
575
  fold qa. rewrite length_qa. lia. }
576
- apply (step_char s kk bits qa' (fa 8) (wcode (fb 0) (fb 1)) (fa 14) (wcode (fb 2) (fb 3))
577
- (beta4 (fb 4) (fb 5) (fb 6) (fb 7)) (fa 20) vsem eq_refl Ep).
578
  + rewrite Hbits. apply decode_qa.
579
  + unfold vsem. rewrite vnet_ok, !cellof_cellv. reflexivity.
580
  Qed.
581
 
582
- Lemma aosem_lt : aosem < 64.
583
- Proof. apply fa_lt. lia. Qed.
584
 
585
- Lemma wget_wn : forall a, a < 64 -> a <> 1 -> a <> 2 -> a <> 3 ->
586
  wget (wnext (win s) aosem vsem) a = if Nat.eqb aosem a then vsem else wget (win s) a.
587
  Proof.
588
  intros a Ha H1 H2 H3. unfold wnext.
@@ -592,7 +656,7 @@ Proof.
592
  apply orb_true_iff in E as [E|E]; [apply orb_true_iff in E as [E|E]|]; apply Nat.eqb_eq in E; lia.
593
  - rewrite wget_upd, Hw. pose proof aosem_lt.
594
  destruct (Nat.eqb_spec aosem a) as [E2|E2].
595
- + rewrite E2, Nat.eqb_refl. replace (a <? 64) with true by (symmetry; apply Nat.ltb_lt; lia). reflexivity.
596
  + replace (Nat.eqb a aosem) with false by (symmetry; apply Nat.eqb_neq; lia). reflexivity.
597
  Qed.
598
 
@@ -600,12 +664,12 @@ Lemma adv_wn : wget (wnext (win s) aosem vsem) AA = advA /\ wget (wnext (win s)
600
  /\ wget (wnext (win s) aosem vsem) AC = advC.
601
  Proof. unfold AA, AR, AC, advA, advR, advC. repeat split; apply wget_wn; lia. Qed.
602
 
603
- Lemma tapes_step : forall qa', contents qa' = rotn 26 qa ->
604
  let t := stepres s kk qa' aosem vsem in
605
  contents (q0 t) = (if kk then (if advA then rot1 (wrote (contents (q0 s)) (Nat.eqb aosem 2))
606
  else wrote (contents (q0 s)) (Nat.eqb aosem 2))
607
- else rotn 26 (contents (q0 s)))
608
- /\ contents (q1 t) = (if kk then rotn 26 (contents (q1 s))
609
  else (if advA then rot1 (wrote (contents (q1 s)) (Nat.eqb aosem 2))
610
  else wrote (contents (q1 s)) (Nat.eqb aosem 2)))
611
  /\ contents (d0 t) = (if kk then (if advC then rot1 (wrotec (contents (d0 s)) (Nat.eqb aosem 3))
@@ -627,29 +691,28 @@ Qed.
627
  End Sem.
628
 
629
  (* ---- the netlist computes the step -------------------------------------------------- *)
630
- Lemma nth_encode : forall s N L i,
631
  length (contents (q0 s)) = N -> length (contents (q1 s)) = N ->
632
  length (contents (d0 s)) = L -> length (contents (d1 s)) = L ->
633
- nth i (encode s) false =
634
- if i <? 61 then wget (win s) (realcell i)
635
- else if i <? 61 + N then nth (i - 61) (contents (q0 s)) false
636
- else if i <? 61 + 2 * N then nth (i - 61 - N) (contents (q1 s)) false
637
- else if i <? 61 + 2 * N + L then nth (i - 61 - 2 * N) (contents (d0 s)) false
638
- else nth (i - 61 - 2 * N - L) (contents (d1 s)) false.
639
- Proof.
640
- intros s N L i H0 H1 H2 H3. unfold encode.
641
- destruct (Nat.ltb_spec i 61).
642
  { rewrite app_nth1 by (rewrite length_map, length_seq; lia).
643
- exact (nth_map_seq _ (fun j => wget (win s) (realcell j)) 61 i false H). }
644
  rewrite app_nth2 by (rewrite length_map, length_seq; lia). rewrite length_map, length_seq.
645
- destruct (Nat.ltb_spec i (61 + N)).
646
  { rewrite app_nth1 by lia. reflexivity. }
647
  rewrite app_nth2 by lia. rewrite H0.
648
- destruct (Nat.ltb_spec i (61 + 2 * N)).
649
  { rewrite app_nth1 by lia. f_equal; lia. }
650
  rewrite app_nth2 by lia. rewrite H1.
651
- destruct (Nat.ltb_spec i (61 + 2 * N + L)).
652
  { rewrite app_nth1 by lia. f_equal; lia. }
653
  rewrite app_nth2 by lia. rewrite H2. f_equal; lia.
654
  Qed.
655
-
 
4
  From TH Require Import TwoHead Tapes Abstract Net Gates Phys PhysWF.
5
 
6
  (* the state bits of a machine state, in the order of the netlist *)
7
+ Definition encode (al : nat) (s : st) : list bool :=
8
+ map (fun j => wget (win s) (realcell j)) (seq 0 (nwin al))
9
  ++ contents (q0 s) ++ contents (q1 s) ++ contents (d0 s) ++ contents (d1 s).
10
 
11
+ Lemma length_encode : forall al s N L,
12
  length (contents (q0 s)) = N -> length (contents (q1 s)) = N ->
13
  length (contents (d0 s)) = L -> length (contents (d1 s)) = L ->
14
+ length (encode al s) = nst al N L.
15
  Proof.
16
+ intros al s N L H1 H2 H3 H4. unfold encode, nst.
17
  rewrite !length_app, length_map, length_seq, H1, H2, H3, H4. lia.
18
  Qed.
19
 
20
+ (* ---- addresses of al bits, most significant first ---------------------------------- *)
21
+ Lemma bits2nat_acc_eq : forall l acc, bits2nat_acc acc l = acc * 2 ^ length l + bits2nat l.
22
+ Proof.
23
+ unfold bits2nat. induction l as [|b l IH]; intro acc; cbn [bits2nat_acc length Nat.pow].
24
+ - lia.
25
+ - rewrite (IH (2 * acc + (if b then 1 else 0))), (IH (2 * 0 + (if b then 1 else 0))). destruct b; nia.
26
+ Qed.
27
+
28
+ Lemma bits2nat_cons : forall b l, bits2nat (b :: l) = (if b then 2 ^ length l else 0) + bits2nat l.
29
+ Proof.
30
+ intros b l. unfold bits2nat at 1. cbn [bits2nat_acc]. rewrite bits2nat_acc_eq. destruct b; lia.
31
+ Qed.
32
+
33
+ Lemma bits2nat_lt : forall l, bits2nat l < 2 ^ length l.
34
+ Proof.
35
+ induction l as [|b l IH]; [cbn; lia|].
36
+ rewrite bits2nat_cons. cbn [length Nat.pow]. destruct b; lia.
37
+ Qed.
38
+
39
+ Lemma testbit_bits2nat : forall l t, t < length l ->
40
+ Nat.testbit (bits2nat l) (length l - 1 - t) = nth t l false.
41
+ Proof.
42
+ induction l as [|b l IH]; intros t Ht; cbn [length] in Ht; [lia|].
43
+ rewrite bits2nat_cons. cbn [length]. pose proof (bits2nat_lt l) as Hlt.
44
+ destruct t as [|t].
45
+ - cbn [nth]. replace (S (length l) - 1 - 0) with (length l) by lia.
46
+ apply Nat.b2n_inj. rewrite Nat.testbit_spec'.
47
+ destruct b.
48
+ + replace (2 ^ length l + bits2nat l) with (1 * 2 ^ length l + bits2nat l) by lia.
49
+ rewrite Nat.div_add_l by (apply Nat.pow_nonzero; lia). rewrite Nat.div_small by exact Hlt. reflexivity.
50
+ + rewrite Nat.add_0_l, Nat.div_small by exact Hlt. reflexivity.
51
+ - cbn [nth]. replace (S (length l) - 1 - S t) with (length l - 1 - t) by lia.
52
+ rewrite <- (Nat.mod_pow2_bits_low _ (length l)) by lia.
53
+ replace ((if b then 2 ^ length l else 0) + bits2nat l) with (bits2nat l + (if b then 1 else 0) * 2 ^ length l)
54
+ by (destruct b; lia).
55
+ rewrite Nat.Div0.mod_add, Nat.mod_small by exact Hlt.
56
+ apply IH. lia.
57
+ Qed.
58
+
59
+ Lemma bits_eq_nat : forall x y w, x < 2 ^ w -> y < 2 ^ w ->
60
+ (forall i, i < w -> Nat.testbit x i = Nat.testbit y i) -> x = y.
61
+ Proof.
62
+ intros x y w Hx Hy H. apply Nat.bits_inj. intro n.
63
+ destruct (Nat.lt_ge_cases n w) as [Hn|Hn]; [apply H; exact Hn|].
64
+ assert (Z0 : forall z, z < 2 ^ w -> Nat.testbit z n = false).
65
+ { intros z Hz. apply Bool.not_true_is_false. intro E.
66
+ pose proof (Nat.testbit_spec' z n) as S. rewrite E in S. cbn [Nat.b2n] in S.
67
+ rewrite Nat.div_small in S; [cbn in S; discriminate|].
68
+ pose proof (Nat.pow_le_mono_r 2 w n ltac:(lia) Hn). lia. }
69
+ rewrite !Z0 by assumption. reflexivity.
70
+ Qed.
71
+
72
+ Lemma dec_okA : forall al f a, length f = al -> a < 2 ^ al ->
73
+ forallb (fun t => Bool.eqb (nth t f false) (bitsof al a t)) (seq 0 al) = Nat.eqb (bits2nat f) a.
74
+ Proof.
75
+ intros al f a Hf Ha. unfold bitsof.
76
+ destruct (forallb (fun t => Bool.eqb (nth t f false) (Nat.testbit a (al - 1 - t))) (seq 0 al)) eqn:E; symmetry.
77
+ - apply Nat.eqb_eq. apply (bits_eq_nat _ _ al); [rewrite <- Hf; apply bits2nat_lt | exact Ha |].
78
+ intros i Hi. rewrite forallb_forall in E. specialize (E (al - 1 - i) ltac:(apply in_seq; lia)).
79
+ apply Bool.eqb_prop in E. replace (al - 1 - (al - 1 - i)) with i in E by lia.
80
+ rewrite <- E. pose proof (testbit_bits2nat f (al - 1 - i) ltac:(lia)) as T.
81
+ rewrite Hf in T. replace (al - 1 - (al - 1 - i)) with i in T by lia. exact T.
82
+ - apply Nat.eqb_neq. intro Eq. subst a.
83
+ assert (Et : forallb (fun t => Bool.eqb (nth t f false) (Nat.testbit (bits2nat f) (al - 1 - t))) (seq 0 al) = true).
84
+ { apply forallb_forall. intros t Ht. apply in_seq in Ht.
85
+ pose proof (testbit_bits2nat f t ltac:(lia)) as T. rewrite Hf in T. rewrite T. apply eqb_reflx. }
86
+ congruence.
87
+ Qed.
88
+
89
+ (* the step of the machine with a window of 2^al cells, unfolded *)
90
+ Lemma step_charA : forall al s k bits qa' a0 w0 a1 w1 beta ao v,
91
+ wget (win s) KAPPA = k ->
92
+ popn (recw al) (if k then q1 s else q0 s) = (bits, qa') ->
93
+ decodeA al bits = Rec a0 w0 a1 w1 beta ao ->
94
+ v = Z.leb 0 (w0 * b2z (cellof s k a0) + w1 * b2z (cellof s k a1) + beta)%Z ->
95
+ stepA al s = stepres s k qa' ao v.
96
+ Proof.
97
+ intros al s k bits qa' a0 w0 a1 w1 beta ao v Hk Hp Hd Hv.
98
+ unfold stepA, stepres, cellof, wnext, clearq. cbv zeta. rewrite Hk, Hp, Hd. subst v. reflexivity.
99
+ Qed.
100
+
101
  Section Sem.
102
+ Variables (al N L : nat) (s : st).
103
+ Hypotheses (Hal : 3 <= al) (HN : recw al <= N) (HL : 1 <= L)
104
  (Hq0 : length (contents (q0 s)) = N) (Hq1 : length (contents (q1 s)) = N)
105
  (Hd0 : length (contents (d0 s)) = L) (Hd1 : length (contents (d1 s)) = L).
106
 
107
+ Let HW8 : 8 <= W al := W_ge al Hal.
108
+ Let HnW : nwin al + 3 = W al := nwin_W al Hal.
109
+ Let Hrw : 8 + 3 * al = recw al := eq_refl.
110
+
111
+ Definition X (i : nat) : bool := nth i (encode al s) false.
112
+ Definition F (x : sref) : bool := run (phys_units al N L) (nst al N L) X (idx (nst al N L) (phys_blocks al N L) x).
113
 
114
+ Lemma X_win : forall j, j < nwin al -> X j = wget (win s) (realcell j).
115
  Proof.
116
  intros j Hj. unfold X, encode. rewrite app_nth1 by (rewrite length_map, length_seq; exact Hj).
117
+ exact (nth_map_seq _ (fun j => wget (win s) (realcell j)) (nwin al) j false Hj).
118
  Qed.
119
 
120
  Lemma X_tape : forall i,
121
+ (i < N -> X (oq0 al + i) = nth i (contents (q0 s)) false)
122
+ /\ (i < N -> X (oq1 al N + i) = nth i (contents (q1 s)) false)
123
+ /\ (i < L -> X (od0 al N + i) = nth i (contents (d0 s)) false)
124
+ /\ (i < L -> X (od1 al N L + i) = nth i (contents (d1 s)) false).
125
  Proof.
126
  intro i. unfold X, encode, oq0, oq1, od0, od1.
127
  repeat split; intro Hi; rewrite app_nth2 by (rewrite length_map, length_seq; lia);
 
132
  - rewrite app_nth2 by lia. rewrite app_nth2 by lia. rewrite app_nth2 by lia. f_equal. lia.
133
  Qed.
134
 
135
+ Lemma F_in : forall i, i < nst al N L -> F (SIn i) = X i.
136
  Proof. intros i H. unfold F, phys_units. apply net_in. exact H. Qed.
137
 
138
  Lemma F_unit : forall b j r, b < 28 ->
139
+ j < bcount (nth b (phys_blocks al N L) blk0) -> r < bsize (nth b (phys_blocks al N L) blk0) ->
140
+ F (SRef b j r) = sfire' (sunit (phys_blocks al N L) b j r) F.
141
  Proof.
142
  intros b j r Hb Hj Hr. unfold F, phys_units. apply net_unit; [apply phys_wf; assumption| |assumption|assumption].
143
  rewrite phys_length. exact Hb.
144
  Qed.
145
 
146
  Lemma F_and : forall b j r ls, b < 28 ->
147
+ j < bcount (nth b (phys_blocks al N L) blk0) -> r < bsize (nth b (phys_blocks al N L) blk0) ->
148
+ sunit (phys_blocks al N L) b j r = sAND ls -> F (SRef b j r) = forallb (litv F) ls.
149
  Proof. intros b j r ls Hb Hj Hr E. rewrite F_unit, E by assumption. apply sAND_fire. Qed.
150
 
151
  Lemma F_or : forall b j r ls, b < 28 ->
152
+ j < bcount (nth b (phys_blocks al N L) blk0) -> r < bsize (nth b (phys_blocks al N L) blk0) ->
153
+ sunit (phys_blocks al N L) b j r = sOR ls -> F (SRef b j r) = existsb (litv F) ls.
154
  Proof. intros b j r ls Hb Hj Hr E. rewrite F_unit, E by assumption. apply sOR_fire. Qed.
155
 
156
  Lemma F_mux : forall b j r0 sel a c, b < 28 ->
157
+ j < bcount (nth b (phys_blocks al N L) blk0) -> S (S r0) < bsize (nth b (phys_blocks al N L) blk0) ->
158
+ sunit (phys_blocks al N L) b j r0 = sAND [(sel, true); (a, true)] ->
159
+ sunit (phys_blocks al N L) b j (S r0) = sAND [(sel, false); (c, true)] ->
160
+ sunit (phys_blocks al N L) b j (S (S r0)) = sOR [(SRef b j r0, true); (SRef b j (S r0), true)] ->
161
  F (SRef b j (S (S r0))) = if F sel then F a else F c.
162
  Proof.
163
  intros b j r0 sel a c Hb Hj Hr E0 E1 E2.
 
178
  blk_desch blk_desc sunit mux3 app].
179
 
180
  (* block 0: the record in front of the active bank *)
181
+ Lemma F_fld : forall j, j < recw al -> F (fld j) = nth j qa false.
182
  Proof.
183
  intros j Hj. unfold fld.
184
+ rewrite (F_mux 0 j 0 kap (SIn (oq1 al N + j)) (SIn (oq0 al + j))) by (blk_compute; first [lia | reflexivity]).
185
  rewrite F_kap, !F_in by (unfold nst, oq0, oq1; lia).
186
  destruct (X_tape j) as (T0 & T1 & _ & _). rewrite T0, T1 by lia.
187
  unfold qa. destruct kk; reflexivity.
 
201
  rewrite <- !plus_n_O in T0, T1, T2, T3.
202
  repeat split;
203
  (rewrite F_mux with (sel := kap)
204
+ (a := SIn (fst (head_src al N L _))) (c := SIn (snd (head_src al N L _))) by (blk_compute; first [lia | reflexivity]));
205
  cbn [head_src fst snd]; rewrite F_kap, !F_in by (unfold nst, oq0, oq1, od0, od1; lia);
206
  rewrite ?T0, ?T1, ?T2, ?T3 by lia; unfold rdv, armv, cpyv; rewrite <- ?nth0_hd0; destruct kk; reflexivity.
207
  Qed.
208
 
209
  (* ---- blocks 2 to 8: the three addresses and the two operands --------------------- *)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  Lemma length_qa : length qa = N.
211
  Proof. unfold qa. destruct kk; assumption. Qed.
212
 
213
+ Definition field (o : nat) : list bool := firstn al (skipn o qa).
214
 
215
+ Lemma length_field : forall o, o + al <= recw al -> length (field o) = al.
216
  Proof. intros o Ho. unfold field. rewrite length_firstn, length_skipn, length_qa. lia. Qed.
217
 
218
+ Lemma nth_field : forall o t, t < al -> nth t (field o) false = nth (o + t) qa false.
219
+ Proof. intros o t Ht. unfold field. rewrite nth_firstn. destruct (Nat.ltb_spec t al); [|lia]. apply nth_skipn. Qed.
220
 
221
  Lemma forallb_map' : forall (A B : Type) (f : B -> bool) (g : A -> B) l, forallb f (map g l) = forallb (fun x => f (g x)) l.
222
  Proof. intros A B f g l. induction l as [|x l IH]; [reflexivity|]. cbn [map forallb]. rewrite IH. reflexivity. Qed.
 
230
  Lemma existsb_ext_in : forall (A : Type) (f g : A -> bool) l, (forall x, In x l -> f x = g x) -> existsb f l = existsb g l.
231
  Proof. intros A f g l H. induction l as [|x l IH]; [reflexivity|]. cbn [existsb]. rewrite H, IH; [reflexivity| |left; reflexivity]. intros y Hy. apply H. right. exact Hy. Qed.
232
 
233
+ Lemma F_dec : forall b o a, (b = 2 /\ o = 8) \/ (b = 3 /\ o = 8 + al) \/ (b = 4 /\ o = 8 + 2 * al) -> a < W al ->
234
  F (SRef b a 0) = Nat.eqb (bits2nat (field o)) a.
235
  Proof.
236
  intros b o a Hbo Ha.
237
+ assert (Hs : sunit (phys_blocks al N L) b a 0 = sAND (map (fun t => (fld (o + t), bitsof al a t)) (seq 0 al)))
238
  by (destruct Hbo as [[-> ->]|[[-> ->]|[-> ->]]]; reflexivity).
239
  rewrite (F_and b a 0 _ ltac:(lia) ltac:(destruct Hbo as [[-> ->]|[[-> ->]|[-> ->]]]; blk_compute; lia)
240
  ltac:(destruct Hbo as [[-> ->]|[[-> ->]|[-> ->]]]; blk_compute; lia) Hs).
241
+ rewrite forallb_map'. rewrite <- (dec_okA al (field o) a) by (try apply length_field; unfold W in Ha; lia).
242
  apply forallb_ext_in. intros t Ht. apply in_seq in Ht.
243
  unfold litv. cbn [fst snd]. rewrite F_fld by lia. rewrite nth_field by lia. reflexivity.
244
  Qed.
 
246
  Definition cellv (a : nat) : bool :=
247
  if Nat.eqb a 1 then rdv else if Nat.eqb a 2 then armv else if Nat.eqb a 3 then cpyv else wget (win s) a.
248
 
249
+ Lemma F_cellref : forall a, a < W al -> F (cellref a) = cellv a.
250
  Proof.
251
  intros a Ha. destruct F_heads as (H1 & H2 & H3). unfold cellref, cellv.
252
  destruct (Nat.eqb_spec a 1) as [->|E1]; [exact H1|].
 
275
 
276
  Definition fa (o : nat) : nat := bits2nat (field o).
277
 
278
+ Lemma fa_lt : forall o, o + al <= recw al -> fa o < W al.
279
  Proof.
280
+ intros o Ho. unfold fa, W. rewrite <- (length_field o Ho). apply bits2nat_lt.
 
 
 
281
  Qed.
282
 
283
+ Lemma F_xs : forall i, i < 2 -> F (xsref i) = cellv (fa (8 + al * i)).
284
  Proof.
285
  intros i Hi. unfold xsref.
 
286
  destruct (Nat.eqb_spec i 0) as [->|E].
287
+ - assert (Hlt := fa_lt 8 ltac:(lia)).
288
+ rewrite (F_or 6 0 0 (map (fun a => (SRef 5 a 0, true)) (seq 0 (W al)))) by (blk_compute; first [lia | reflexivity]).
289
  rewrite existsb_map'.
290
  rewrite (existsb_ext_in _ _ (fun a => Nat.eqb (fa 8) a && cellv a)).
291
+ + rewrite existsb_pick. rewrite Nat.mul_0_r, Nat.add_0_r.
292
  replace (0 <=? fa 8) with true by (symmetry; apply Nat.leb_le; lia).
293
+ replace (fa 8 <? 0 + W al) with true by (symmetry; apply Nat.ltb_lt; lia).
294
  reflexivity.
295
  + intros a Ha. apply in_seq in Ha. rewrite litv_pos.
296
  rewrite (F_and 5 a 0 [(SRef 2 a 0, true); (cellref a, true)]) by (blk_compute; first [lia | reflexivity]).
297
  cbn [forallb]. rewrite !litv_pos, andb_true_r.
298
  rewrite F_dec with (o := 8) by (lia || (left; split; reflexivity)).
299
  rewrite F_cellref by lia. reflexivity.
300
+ - replace i with 1 in * by lia. rewrite Nat.mul_1_r.
301
+ assert (Hlt := fa_lt (8 + al) ltac:(lia)).
302
+ rewrite (F_or 8 0 0 (map (fun a => (SRef 7 a 0, true)) (seq 0 (W al)))) by (blk_compute; first [lia | reflexivity]).
303
  rewrite existsb_map'.
304
+ rewrite (existsb_ext_in _ _ (fun a => Nat.eqb (fa (8 + al)) a && cellv a)).
305
+ + rewrite existsb_pick.
306
+ replace (0 <=? fa (8 + al)) with true by (symmetry; apply Nat.leb_le; lia).
307
+ replace (fa (8 + al) <? 0 + W al) with true by (symmetry; apply Nat.ltb_lt; lia).
308
  reflexivity.
309
  + intros a Ha. apply in_seq in Ha. rewrite litv_pos.
310
  rewrite (F_and 7 a 0 [(SRef 3 a 0, true); (cellref a, true)]) by (blk_compute; first [lia | reflexivity]).
311
  cbn [forallb]. rewrite !litv_pos, andb_true_r.
312
+ rewrite F_dec with (o := 8 + al) by (lia || (right; left; split; reflexivity)).
313
  rewrite F_cellref by lia. reflexivity.
314
  Qed.
315
 
 
333
  = Z.leb 0 (wcode f0 f1 * b2z y0 + wcode f2 f3 * b2z y1 + beta4 f4 f5 f6 f7)%Z.
334
  Proof. intros. destruct f0, f1, f2, f3, f4, f5, f6, f7, y0, y1; reflexivity. Qed.
335
 
336
+ Lemma F_fb : forall j, j < recw al -> F (fld j) = fb j.
337
  Proof. exact F_fld. Qed.
338
 
339
  Lemma F_v : F (SRef 17 0 0) =
 
389
  Qed.
390
 
391
  (* ---- blocks 18 and 19: the window after the write --------------------------------- *)
392
+ Definition vsem : bool :=
393
+ vnet (fb 0) (fb 1) (fb 2) (fb 3) (fb 4) (fb 5) (fb 6) (fb 7) (cellv (fa 8)) (cellv (fa (8 + al))).
394
+ Definition aosem : nat := fa (8 + 2 * al).
395
 
396
  Lemma F_vsem : F (SRef 17 0 0) = vsem.
397
+ Proof.
398
+ rewrite F_v, (F_xs 0), (F_xs 1) by lia. rewrite Nat.mul_0_r, Nat.add_0_r, Nat.mul_1_r. reflexivity.
399
+ Qed.
400
 
401
+ Lemma F_lo : forall a, a < W al -> F (SRef 4 a 0) = Nat.eqb aosem a.
402
  Proof. intros a Ha. apply F_dec; [right; right; split; reflexivity | exact Ha]. Qed.
403
 
404
+ Lemma realcell_lt : forall j, j < nwin al -> realcell j < W al.
405
  Proof. intros j Hj. unfold realcell. destruct (Nat.eqb j 0); lia. Qed.
406
 
407
+ Lemma F_xhat : forall j, j < nwin al ->
408
  F (SRef 18 j 2) = if Nat.eqb aosem (realcell j) then vsem else wget (win s) (realcell j).
409
  Proof.
410
  intros j Hj.
 
453
  destruct here; destruct m; reflexivity.
454
  Qed.
455
 
456
+ Lemma F_bank : forall bh bb idlep base T, (bh = 20 /\ bb = 21 /\ idlep = true /\ base = oq0 al /\ T = q0 s)
457
+ \/ (bh = 22 /\ bb = 23 /\ idlep = false /\ base = oq1 al N /\ T = q1 s) ->
458
  forall i, i < N ->
459
  F (SRef bb i 3) =
460
  if Bool.eqb kk idlep
461
  then nth i (if advA then rot1 (wrote (contents T) (Nat.eqb aosem 2)) else wrote (contents T) (Nat.eqb aosem 2)) false
462
+ else nth i (rotn (recw al) (contents T)) false.
463
  Proof.
464
  intros bh bb idlep base T Hsel i Hi.
465
  assert (HT : length (contents T) = N) by (destruct Hsel as [(_ & _ & _ & _ & ->)|(_ & _ & _ & _ & ->)]; assumption).
466
  assert (Hx : forall m, m < N -> X (base + m) = nth m (contents T) false).
467
  { intros m Hm. destruct (X_tape m) as (T0 & T1 & _ & _).
468
  destruct Hsel as [(_ & _ & _ & -> & ->)|(_ & _ & _ & -> & ->)]; [apply T0|apply T1]; exact Hm. }
469
+ assert (Hb : base + N <= nst al N L) by (destruct Hsel as [(_ & _ & _ & -> & _)|(_ & _ & _ & -> & _)]; unfold nst, oq0, oq1; lia).
470
+ assert (Hc : forall j r, j < N -> r < 4 -> bb < 28 /\ j < bcount (nth bb (phys_blocks al N L) blk0)
471
+ /\ r < bsize (nth bb (phys_blocks al N L) blk0))
472
  by (intros; destruct Hsel as [(_ & -> & _)|(_ & -> & _)]; blk_compute; lia).
473
+ assert (Hh : forall r, r < 6 -> bh < 28 /\ 0 < bcount (nth bh (phys_blocks al N L) blk0)
474
+ /\ r < bsize (nth bh (phys_blocks al N L) blk0))
475
  by (intros; destruct Hsel as [(-> & _)|(-> & _)]; blk_compute; lia).
476
+ assert (Hsu : forall j, sunit (phys_blocks al N L) bb j 3 = sOR [(SRef bb j 0, true); (SRef bb j 1, true); (SRef bb j 2, true)]
477
+ /\ sunit (phys_blocks al N L) bb j 0 = sAND [(kap, negb idlep); (SIn (base + (j + recw al) mod N), true)]
478
+ /\ sunit (phys_blocks al N L) bb j 1 = sAND [(SRef bh 0 0, true); (hatq bh base ((j + 1) mod N), true)]
479
+ /\ sunit (phys_blocks al N L) bb j 2 = sAND [(SRef bh 0 1, true); (hatq bh base j, true)])
480
  by (intro j; destruct Hsel as [(-> & -> & -> & -> & _)|(-> & -> & -> & -> & _)]; repeat split; reflexivity).
481
  destruct (Hsu i) as (S3 & S0 & S1 & S2).
482
  destruct (Hc i 3 Hi ltac:(lia)) as (C1 & C2 & C3).
 
491
  { destruct F_adv as (_ & HA & _).
492
  destruct (Hh 0 ltac:(lia)) as (H01 & H02 & H03). destruct (Hh 1 ltac:(lia)) as (H11 & H12 & H13).
493
  destruct (Hh 2 ltac:(lia)) as (H21 & H22 & H23). destruct (Hh 5 ltac:(lia)) as (H51 & H52 & H53).
494
+ assert (U : sunit (phys_blocks al N L) bh 0 0 = sAND [(kap, idlep); (SRef 18 2 2, true)]
495
+ /\ sunit (phys_blocks al N L) bh 0 1 = sAND [(kap, idlep); (SRef 18 2 2, false)]
496
+ /\ sunit (phys_blocks al N L) bh 0 2 = sAND [(SRef 4 2 0, true); (kap, idlep)]
497
+ /\ sunit (phys_blocks al N L) bh 0 3 = sAND [(SRef bh 0 2, true); (SRef 17 0 0, true)]
498
+ /\ sunit (phys_blocks al N L) bh 0 4 = sAND [(SRef bh 0 2, false); (SIn base, true)]
499
+ /\ sunit (phys_blocks al N L) bh 0 5 = sOR [(SRef bh 0 3, true); (SRef bh 0 4, true)])
500
  by (destruct Hsel as [(-> & -> & -> & -> & _)|(-> & -> & -> & -> & _)]; repeat split; reflexivity).
501
  destruct U as (U0 & U1 & U2 & U3 & U4 & U5).
502
  split; [|split].
 
515
  { intros m Hm. rewrite nth_wrote by lia. unfold hatq. destruct (Nat.eqb_spec m 0) as [->|E].
516
  - rewrite Hh0. reflexivity.
517
  - rewrite F_in by lia. apply Hx. exact Hm. }
518
+ pose proof (Nat.mod_upper_bound (i + 1) N ltac:(lia)). pose proof (Nat.mod_upper_bound (i + recw al) N ltac:(lia)).
519
  rewrite Hia, Hih, !Hhat by lia. unfold litv. cbn [fst snd]. rewrite F_kap, F_in by lia. rewrite Hx by lia.
520
  destruct (Bool.eqb kk idlep) eqn:Ek.
521
  - apply Bool.eqb_prop in Ek. subst idlep. rewrite ?eqb_reflx, ?andb_true_r.
 
533
  (* ---- blocks 24 to 27: the description tapes -------------------------------------- *)
534
  Definition wrotec (l : list bool) (here : bool) : list bool := if here then wr0 l vsem else l.
535
 
536
+ Lemma F_desc : forall dh db idlep base T, (dh = 24 /\ db = 25 /\ idlep = true /\ base = od0 al N /\ T = d0 s)
537
+ \/ (dh = 26 /\ db = 27 /\ idlep = false /\ base = od1 al N L /\ T = d1 s) ->
538
  forall i, i < L ->
539
  F (SRef db i 2) =
540
  if Bool.eqb kk idlep
 
546
  assert (Hx : forall m, m < L -> X (base + m) = nth m (contents T) false).
547
  { intros m Hm. destruct (X_tape m) as (_ & _ & T2 & T3).
548
  destruct Hsel as [(_ & _ & _ & -> & ->)|(_ & _ & _ & -> & ->)]; [apply T2|apply T3]; exact Hm. }
549
+ assert (Hb : base + L <= nst al N L)
550
  by (destruct Hsel as [(_ & _ & _ & -> & _)|(_ & _ & _ & -> & _)]; unfold nst, od0, od1; lia).
551
+ assert (Hh : forall r, r < 7 -> dh < 28 /\ 0 < bcount (nth dh (phys_blocks al N L) blk0)
552
+ /\ r < bsize (nth dh (phys_blocks al N L) blk0))
553
  by (intros; destruct Hsel as [(-> & _)|(-> & _)]; blk_compute; lia).
554
+ assert (U : sunit (phys_blocks al N L) dh 0 0 = sAND [(kap, negb idlep); (SRef 18 1 2, true)]
555
+ /\ sunit (phys_blocks al N L) dh 0 1 = sAND [(kap, idlep); (SRef 18 3 2, true)]
556
+ /\ sunit (phys_blocks al N L) dh 0 2 = sOR [(SRef dh 0 0, true); (SRef dh 0 1, true)]
557
+ /\ sunit (phys_blocks al N L) dh 0 3 = sAND [(SRef 4 3 0, true); (kap, idlep)]
558
+ /\ sunit (phys_blocks al N L) dh 0 4 = sAND [(SRef dh 0 3, true); (SRef 17 0 0, true)]
559
+ /\ sunit (phys_blocks al N L) dh 0 5 = sAND [(SRef dh 0 3, false); (SIn base, true)]
560
+ /\ sunit (phys_blocks al N L) dh 0 6 = sOR [(SRef dh 0 4, true); (SRef dh 0 5, true)])
561
  by (destruct Hsel as [(-> & -> & -> & -> & _)|(-> & -> & -> & -> & _)]; repeat split; reflexivity).
562
  destruct U as (U0 & U1 & U2 & U3 & U4 & U5 & U6).
563
  destruct F_adv as (HR & _ & HC).
 
583
  - rewrite Hh0. destruct (Nat.eqb aosem 3 && Bool.eqb kk idlep); reflexivity.
584
  - rewrite F_in by lia. rewrite Hx by exact Hm.
585
  destruct (Nat.eqb aosem 3 && Bool.eqb kk idlep); [destruct m; [lia|]|]; reflexivity. }
586
+ assert (Hm3 : sunit (phys_blocks al N L) db i 0 = sAND [(SRef dh 0 2, true); (hatd dh base ((i + 1) mod L), true)]
587
+ /\ sunit (phys_blocks al N L) db i 1 = sAND [(SRef dh 0 2, false); (hatd dh base i, true)]
588
+ /\ sunit (phys_blocks al N L) db i 2 = sOR [(SRef db i 0, true); (SRef db i 1, true)])
589
  by (destruct Hsel as [(-> & -> & _ & -> & _)|(-> & -> & _ & -> & _)]; repeat split; reflexivity).
590
  destruct Hm3 as (M0 & M1 & M2).
591
+ assert (Hc : db < 28 /\ i < bcount (nth db (phys_blocks al N L) blk0) /\ 2 < bsize (nth db (phys_blocks al N L) blk0))
592
  by (destruct Hsel as [(_ & -> & _)|(_ & -> & _)]; blk_compute; lia).
593
  destruct Hc as (C1 & C2 & C3).
594
  rewrite (F_mux db i 0 (SRef dh 0 2) (hatd dh base ((i + 1) mod L)) (hatd dh base i) C1 C2 C3 M0 M1 M2).
 
602
  Qed.
603
 
604
  (* ---- one step of the machine in the same terms ------------------------------------ *)
605
+ Hypothesis Hw : length (win s) = W al.
606
 
607
  Lemma firstn_skipn_firstn : forall (l : list bool) a b c, a + b <= c ->
608
  firstn b (skipn a (firstn c l)) = firstn b (skipn a l).
 
615
  reflexivity.
616
  Qed.
617
 
618
+ Lemma decode_qa : decodeA al (firstn (recw al) qa) =
619
+ Rec (fa 8) (wcode (fb 0) (fb 1)) (fa (8 + al)) (wcode (fb 2) (fb 3)) (beta4 (fb 4) (fb 5) (fb 6) (fb 7))
620
+ (fa (8 + 2 * al)).
621
  Proof.
622
+ assert (Hn : forall i, i < 8 -> nth i (firstn (recw al) qa) false = nth i qa false).
623
+ { intros i Hi. rewrite nth_firstn. destruct (Nat.ltb_spec i (recw al)); [reflexivity | unfold recw in *; lia]. }
624
+ unfold decodeA. cbv zeta. unfold fa, field, fb, beta4.
625
+ rewrite !firstn_skipn_firstn by (unfold recw; lia). rewrite !Hn by lia.
626
+ rewrite firstn4_skipn4 by (rewrite length_qa; unfold recw in *; lia). reflexivity.
627
  Qed.
628
 
629
  Lemma cellof_cellv : forall a, cellof s kk a = cellv a.
630
  Proof. intro a. unfold cellof, cellv, rdv, armv, cpyv. rewrite !hd_t_contents. reflexivity. Qed.
631
 
632
+ Lemma step_eq : exists qa', contents qa' = rotn (recw al) qa /\ stepA al s = stepres s kk qa' aosem vsem.
633
  Proof.
634
+ destruct (popn (recw al) (if kk then q1 s else q0 s)) as [bits qa'] eqn:Ep.
635
  exists qa'. split.
636
+ - pose proof (popn_turns (recw al) (if kk then q1 s else q0 s)) as P. rewrite Ep in P. exact P.
637
+ - assert (Hbits : bits = firstn (recw al) qa).
638
+ { pose proof (popn_reads (recw al) (if kk then q1 s else q0 s)) as P. rewrite Ep in P. apply P.
639
  fold qa. rewrite length_qa. lia. }
640
+ apply (step_charA al s kk bits qa' (fa 8) (wcode (fb 0) (fb 1)) (fa (8 + al)) (wcode (fb 2) (fb 3))
641
+ (beta4 (fb 4) (fb 5) (fb 6) (fb 7)) (fa (8 + 2 * al)) vsem eq_refl Ep).
642
  + rewrite Hbits. apply decode_qa.
643
  + unfold vsem. rewrite vnet_ok, !cellof_cellv. reflexivity.
644
  Qed.
645
 
646
+ Lemma aosem_lt : aosem < W al.
647
+ Proof. apply fa_lt. unfold recw. lia. Qed.
648
 
649
+ Lemma wget_wn : forall a, a < W al -> a <> 1 -> a <> 2 -> a <> 3 ->
650
  wget (wnext (win s) aosem vsem) a = if Nat.eqb aosem a then vsem else wget (win s) a.
651
  Proof.
652
  intros a Ha H1 H2 H3. unfold wnext.
 
656
  apply orb_true_iff in E as [E|E]; [apply orb_true_iff in E as [E|E]|]; apply Nat.eqb_eq in E; lia.
657
  - rewrite wget_upd, Hw. pose proof aosem_lt.
658
  destruct (Nat.eqb_spec aosem a) as [E2|E2].
659
+ + rewrite E2, Nat.eqb_refl. replace (a <? W al) with true by (symmetry; apply Nat.ltb_lt; lia). reflexivity.
660
  + replace (Nat.eqb a aosem) with false by (symmetry; apply Nat.eqb_neq; lia). reflexivity.
661
  Qed.
662
 
 
664
  /\ wget (wnext (win s) aosem vsem) AC = advC.
665
  Proof. unfold AA, AR, AC, advA, advR, advC. repeat split; apply wget_wn; lia. Qed.
666
 
667
+ Lemma tapes_step : forall qa', contents qa' = rotn (recw al) qa ->
668
  let t := stepres s kk qa' aosem vsem in
669
  contents (q0 t) = (if kk then (if advA then rot1 (wrote (contents (q0 s)) (Nat.eqb aosem 2))
670
  else wrote (contents (q0 s)) (Nat.eqb aosem 2))
671
+ else rotn (recw al) (contents (q0 s)))
672
+ /\ contents (q1 t) = (if kk then rotn (recw al) (contents (q1 s))
673
  else (if advA then rot1 (wrote (contents (q1 s)) (Nat.eqb aosem 2))
674
  else wrote (contents (q1 s)) (Nat.eqb aosem 2)))
675
  /\ contents (d0 t) = (if kk then (if advC then rot1 (wrotec (contents (d0 s)) (Nat.eqb aosem 3))
 
691
  End Sem.
692
 
693
  (* ---- the netlist computes the step -------------------------------------------------- *)
694
+ Lemma nth_encode : forall al s N L i,
695
  length (contents (q0 s)) = N -> length (contents (q1 s)) = N ->
696
  length (contents (d0 s)) = L -> length (contents (d1 s)) = L ->
697
+ nth i (encode al s) false =
698
+ if i <? nwin al then wget (win s) (realcell i)
699
+ else if i <? nwin al + N then nth (i - nwin al) (contents (q0 s)) false
700
+ else if i <? nwin al + 2 * N then nth (i - nwin al - N) (contents (q1 s)) false
701
+ else if i <? nwin al + 2 * N + L then nth (i - nwin al - 2 * N) (contents (d0 s)) false
702
+ else nth (i - nwin al - 2 * N - L) (contents (d1 s)) false.
703
+ Proof.
704
+ intros al s N L i H0 H1 H2 H3. unfold encode.
705
+ destruct (Nat.ltb_spec i (nwin al)).
706
  { rewrite app_nth1 by (rewrite length_map, length_seq; lia).
707
+ exact (nth_map_seq _ (fun j => wget (win s) (realcell j)) (nwin al) i false H). }
708
  rewrite app_nth2 by (rewrite length_map, length_seq; lia). rewrite length_map, length_seq.
709
+ destruct (Nat.ltb_spec i (nwin al + N)).
710
  { rewrite app_nth1 by lia. reflexivity. }
711
  rewrite app_nth2 by lia. rewrite H0.
712
+ destruct (Nat.ltb_spec i (nwin al + 2 * N)).
713
  { rewrite app_nth1 by lia. f_equal; lia. }
714
  rewrite app_nth2 by lia. rewrite H1.
715
+ destruct (Nat.ltb_spec i (nwin al + 2 * N + L)).
716
  { rewrite app_nth1 by lia. f_equal; lia. }
717
  rewrite app_nth2 by lia. rewrite H2. f_equal; lia.
718
  Qed.
 
rocq/PhysWF.v CHANGED
@@ -3,15 +3,24 @@ From Stdlib Require Import List Bool Arith ZArith Lia.
3
  Import ListNotations.
4
  From TH Require Import TwoHead Net Gates Phys.
5
 
6
- Lemma phys_shaped : forall N L, Forall shaped (phys_blocks N L).
7
  Proof.
8
- intros N L. unfold phys_blocks.
9
  repeat apply Forall_cons; try apply Forall_nil; intros j Hj; reflexivity.
10
  Qed.
11
 
12
- Lemma phys_length : forall N L, length (phys_blocks N L) = 28.
13
  Proof. reflexivity. Qed.
14
 
 
 
 
 
 
 
 
 
 
15
  Ltac unfold_layout := unfold nst, oq0, oq1, od0, od1 in *.
16
 
17
  (* a reference to an earlier block, or to an earlier unit of the same member, or a state bit *)
@@ -23,17 +32,17 @@ Ltac rb_solve :=
23
  | left; split; [lia|];
24
  cbn [nth phys_blocks bcount bsize blk_fld blk_head blk_dec blk_term blk_xs blk_pos blk_neg blk_mt
25
  blk_nt blk_pge blk_b4 blk_bge blk_vand blk_v blk_xhat blk_zero blk_bankh blk_bank blk_desch blk_desc];
26
- split; lia ].
27
 
28
  Ltac crush_in Hin :=
29
- cbn [nth mux3 sin sAND sOR map lw fst snd seq app kap fld xsref vand_bge] in Hin;
30
  repeat (destruct Hin as [Hin|Hin]; [injection Hin as <- <-|]); try destruct Hin.
31
 
32
  (* open the helpers that name signals, splitting on the cases they distinguish *)
33
  Ltac open_refs :=
34
  unfold kap, hatq, hatd, cellref, xsref, realcell in *;
35
  try match goal with |- context [vand_bge ?j] => destruct j as [|[|[|[|j]]]]; cbn [vand_bge] end;
36
- try match goal with |- context [head_src _ _ ?j] => destruct j as [|[|j]]; cbn [head_src fst snd] end;
37
  repeat match goal with |- context [if ?c then _ else _] => destruct c eqn:? end;
38
  repeat match goal with
39
  | H : (_ =? _) = true |- _ => apply Nat.eqb_eq in H
@@ -49,14 +58,67 @@ Ltac split_blocks b :=
49
  blk_desch blk_desc] in *
50
  | first [lia | split_blocks b] ].
51
 
52
- Lemma phys_wf : forall N L, 26 <= N -> 1 <= L -> wf (phys_blocks N L) (nst N L).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  Proof.
54
- intros N L HN HL. split; [apply phys_shaped|].
55
  intros b j r x w Hb Hj Hr Hin. unfold sunit in Hin. rewrite phys_length in Hb.
56
- assert (M1 : forall i, (i + 26) mod N < N) by (intro i; apply Nat.mod_upper_bound; lia).
57
- assert (M2 : forall i, (i + 1) mod N < N) by (intro i; apply Nat.mod_upper_bound; lia).
 
58
  assert (M3 : forall i, (i + 1) mod L < L) by (intro i; apply Nat.mod_upper_bound; lia).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  split_blocks b.
 
60
  all: repeat (destruct r as [|r]; [crush_in Hin | try (exfalso; lia)]).
61
  all: try lia.
62
  all: try rb_solve.
 
3
  Import ListNotations.
4
  From TH Require Import TwoHead Net Gates Phys.
5
 
6
+ Lemma phys_shaped : forall al N L, Forall shaped (phys_blocks al N L).
7
  Proof.
8
+ intros al N L. unfold phys_blocks.
9
  repeat apply Forall_cons; try apply Forall_nil; intros j Hj; reflexivity.
10
  Qed.
11
 
12
+ Lemma phys_length : forall al N L, length (phys_blocks al N L) = 28.
13
  Proof. reflexivity. Qed.
14
 
15
+ Lemma W_ge : forall al, 3 <= al -> 8 <= W al.
16
+ Proof.
17
+ intros al H. unfold W. replace al with (3 + (al - 3)) by lia. rewrite Nat.pow_add_r.
18
+ pose proof (Nat.pow_nonzero 2 (al - 3) ltac:(lia)). cbn [Nat.pow]. lia.
19
+ Qed.
20
+
21
+ Lemma nwin_W : forall al, 3 <= al -> nwin al + 3 = W al.
22
+ Proof. intros al H. pose proof (W_ge al H). unfold nwin. lia. Qed.
23
+
24
  Ltac unfold_layout := unfold nst, oq0, oq1, od0, od1 in *.
25
 
26
  (* a reference to an earlier block, or to an earlier unit of the same member, or a state bit *)
 
32
  | left; split; [lia|];
33
  cbn [nth phys_blocks bcount bsize blk_fld blk_head blk_dec blk_term blk_xs blk_pos blk_neg blk_mt
34
  blk_nt blk_pge blk_b4 blk_bge blk_vand blk_v blk_xhat blk_zero blk_bankh blk_bank blk_desch blk_desc];
35
+ unfold recw in *; split; lia ].
36
 
37
  Ltac crush_in Hin :=
38
+ cbn [nth mux3 sin sAND sOR map lw fst snd app kap fld xsref vand_bge] in Hin;
39
  repeat (destruct Hin as [Hin|Hin]; [injection Hin as <- <-|]); try destruct Hin.
40
 
41
  (* open the helpers that name signals, splitting on the cases they distinguish *)
42
  Ltac open_refs :=
43
  unfold kap, hatq, hatd, cellref, xsref, realcell in *;
44
  try match goal with |- context [vand_bge ?j] => destruct j as [|[|[|[|j]]]]; cbn [vand_bge] end;
45
+ try match goal with |- context [head_src _ _ _ ?j] => destruct j as [|[|j]]; cbn [head_src fst snd] end;
46
  repeat match goal with |- context [if ?c then _ else _] => destruct c eqn:? end;
47
  repeat match goal with
48
  | H : (_ =? _) = true |- _ => apply Nat.eqb_eq in H
 
58
  blk_desch blk_desc] in *
59
  | first [lia | split_blocks b] ].
60
 
61
+ (* the decode lines and the disjunctions of the operands read lists of symbolic length *)
62
+ Lemma in_dec_lits : forall al o a x w,
63
+ In (x, w) (sin (sAND (map (fun t => (fld (o + t), bitsof al a t)) (seq 0 al)))) ->
64
+ exists t, t < al /\ x = fld (o + t).
65
+ Proof.
66
+ intros al o a x w Hin. cbn [sin sAND] in Hin.
67
+ apply in_map_iff in Hin as (l & El & Hl). apply in_map_iff in Hl as (t & <- & Ht).
68
+ apply in_seq in Ht. unfold lw in El. cbn [fst] in El. injection El as Ex _.
69
+ exists t. split; [lia | symmetry; exact Ex].
70
+ Qed.
71
+
72
+ Lemma in_xs_lits : forall bterm al x w,
73
+ In (x, w) (sin (sOR (map (fun a => (SRef bterm a 0, true)) (seq 0 (W al))))) ->
74
+ exists a, a < W al /\ x = SRef bterm a 0.
75
+ Proof.
76
+ intros bterm al x w Hin. cbn [sin sOR] in Hin.
77
+ apply in_map_iff in Hin as (l & El & Hl). apply in_map_iff in Hl as (a & <- & Ha).
78
+ apply in_seq in Ha. unfold lw in El. cbn [fst] in El. injection El as Ex _.
79
+ exists a. split; [lia | symmetry; exact Ex].
80
+ Qed.
81
+
82
+ Lemma phys_wf : forall al N L, 3 <= al -> recw al <= N -> 1 <= L -> wf (phys_blocks al N L) (nst al N L).
83
  Proof.
84
+ intros al N L Hal HN HL. split; [apply phys_shaped|].
85
  intros b j r x w Hb Hj Hr Hin. unfold sunit in Hin. rewrite phys_length in Hb.
86
+ pose proof (nwin_W al Hal) as HW. pose proof (W_ge al Hal) as HW8.
87
+ assert (M1 : forall i, (i + recw al) mod N < N) by (intro i; apply Nat.mod_upper_bound; unfold recw in *; lia).
88
+ assert (M2 : forall i, (i + 1) mod N < N) by (intro i; apply Nat.mod_upper_bound; unfold recw in *; lia).
89
  assert (M3 : forall i, (i + 1) mod L < L) by (intro i; apply Nat.mod_upper_bound; lia).
90
+ (* blocks 2, 3, 4: the decode lines *)
91
+ assert (Hdec : forall o, o <= 8 + 2 * al -> b <> 0 -> b < 5 ->
92
+ In (x, w) (sin (sAND (map (fun t => (fld (o + t), bitsof al j t)) (seq 0 al)))) ->
93
+ reads_back (phys_blocks al N L) (nst al N L) b j r x).
94
+ { intros o Ho Hb0 Hb5 Hin'. destruct (in_dec_lits al o j x w Hin') as (t & Ht & ->).
95
+ unfold fld. cbn [reads_back]. left. split; [lia|].
96
+ cbn [nth phys_blocks bcount bsize blk_fld]. unfold recw. split; lia. }
97
+ (* blocks 6 and 8: the operands *)
98
+ assert (Hxs : forall bterm, bterm < b -> bcount (nth bterm (phys_blocks al N L) blk0) = W al ->
99
+ bsize (nth bterm (phys_blocks al N L) blk0) = 1 ->
100
+ In (x, w) (sin (sOR (map (fun a => (SRef bterm a 0, true)) (seq 0 (W al))))) ->
101
+ reads_back (phys_blocks al N L) (nst al N L) b j r x).
102
+ { intros bterm Hbt Hc Hs Hin'. destruct (in_xs_lits bterm al x w Hin') as (a & Ha & ->).
103
+ cbn [reads_back]. left. rewrite Hc, Hs. lia. }
104
+ destruct (Nat.eq_dec b 2) as [->|N2].
105
+ { cbn [nth phys_blocks bcount bsize bshape blk_dec] in *. destruct r as [|r]; [|lia].
106
+ apply (Hdec 8); [lia | lia | lia | exact Hin]. }
107
+ destruct (Nat.eq_dec b 3) as [->|N3].
108
+ { cbn [nth phys_blocks bcount bsize bshape blk_dec] in *. destruct r as [|r]; [|lia].
109
+ apply (Hdec (8 + al)); [lia | lia | lia | exact Hin]. }
110
+ destruct (Nat.eq_dec b 4) as [->|N4].
111
+ { cbn [nth phys_blocks bcount bsize bshape blk_dec] in *. destruct r as [|r]; [|lia].
112
+ apply (Hdec (8 + 2 * al)); [lia | lia | lia | exact Hin]. }
113
+ destruct (Nat.eq_dec b 6) as [->|N6].
114
+ { cbn [nth phys_blocks bcount bsize bshape blk_xs] in *. destruct r as [|r]; [|lia].
115
+ apply (Hxs 5); [lia | reflexivity | reflexivity | exact Hin]. }
116
+ destruct (Nat.eq_dec b 8) as [->|N8].
117
+ { cbn [nth phys_blocks bcount bsize bshape blk_xs] in *. destruct r as [|r]; [|lia].
118
+ apply (Hxs 7); [lia | reflexivity | reflexivity | exact Hin]. }
119
+ clear Hdec Hxs.
120
  split_blocks b.
121
+ all: try (exfalso; lia).
122
  all: repeat (destruct r as [|r]; [crush_in Hin | try (exfalso; lia)]).
123
  all: try lia.
124
  all: try rb_solve.
rocq/TwoHead.v CHANGED
@@ -121,6 +121,60 @@ Fixpoint iterp (p : positive) (s : st) : st :=
121
  | xI q => step (iterp q (iterp q s))
122
  end.
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  (* ---- the code of descriptions ----------------------------------------- *)
125
  (* kind 0 AND, 1 OR, 2 COPY; s0, s1 the polarities of the two literals. *)
126
  Definition gate_bits (kind : nat) (s0 s1 : bool) : list bool :=
 
121
  | xI q => step (iterp q (iterp q s))
122
  end.
123
 
124
+ (* ---- a window of 2^al cells: records of 8 + 3 al bits ------------------------ *)
125
+ Definition recw (al : nat) : nat := 8 + 3 * al.
126
+
127
+ Definition decodeA (al : nat) (bs : list bool) : rec :=
128
+ let g i := nth i bs false in
129
+ let braw := Z.of_nat (bits2nat (firstn 4 (skipn 4 bs))) in
130
+ Rec (bits2nat (firstn al (skipn 8 bs))) (wcode (g 0%nat) (g 1%nat))
131
+ (bits2nat (firstn al (skipn (8 + al) bs))) (wcode (g 2%nat) (g 3%nat))
132
+ (if Z.leb 8 braw then braw - 16 else braw)%Z
133
+ (bits2nat (firstn al (skipn (8 + 2 * al) bs))).
134
+
135
+ Definition enc_recA (al : nat) (r : rec) : list bool :=
136
+ match r with
137
+ | Rec a0 w0 a1 w1 beta ao =>
138
+ enc_w w0 ++ enc_w w1 ++ nat2bits 4 (Z.to_nat (Z.modulo beta 16))
139
+ ++ nat2bits al a0 ++ nat2bits al a1 ++ nat2bits al ao
140
+ end.
141
+
142
+ Definition stepA (al : nat) (s : st) : st :=
143
+ let k := wget (win s) KAPPA in
144
+ let qa := if k then q1 s else q0 s in
145
+ let qi := if k then q0 s else q1 s in
146
+ let da := if k then d1 s else d0 s in
147
+ let di := if k then d0 s else d1 s in
148
+ let (bits, qa') := popn (recw al) qa in
149
+ match decodeA al bits with
150
+ | Rec a0 w0 a1 w1 beta ao =>
151
+ let cell a :=
152
+ if Nat.eqb a RD then hd_t da
153
+ else if Nat.eqb a ARM then hd_t qi
154
+ else if Nat.eqb a CPY then hd_t di
155
+ else wget (win s) a in
156
+ let v := Z.leb 0 (w0 * b2z (cell a0) + w1 * b2z (cell a1) + beta)%Z in
157
+ let wn := if Nat.eqb ao RD || Nat.eqb ao ARM || Nat.eqb ao CPY then win s
158
+ else upd (win s) ao v in
159
+ let qi1 := if Nat.eqb ao ARM then wr_t qi v else qi in
160
+ let di1 := if Nat.eqb ao CPY then wr_t di v else di in
161
+ let qi2 := if wget wn AA then turn1 qi1 else qi1 in
162
+ let da2 := if wget wn AR then turn1 da else da in
163
+ let di2 := if wget wn AC then turn1 di1 else di1 in
164
+ let w2 := upd (upd (upd wn AA false) AR false) AC false in
165
+ if k then mkS w2 qi2 qa' di2 da2 else mkS w2 qa' qi2 da2 di2
166
+ end.
167
+
168
+ (* the machine with 64 cells is the case al = 6 *)
169
+ Lemma stepA_6 : forall s, stepA 6 s = step s.
170
+ Proof. intro s. reflexivity. Qed.
171
+
172
+ Lemma decodeA_6 : forall bs, decodeA 6 bs = decode bs.
173
+ Proof. intro bs. reflexivity. Qed.
174
+
175
+ Lemma enc_recA_6 : forall r, enc_recA 6 r = enc_rec r.
176
+ Proof. intro r. reflexivity. Qed.
177
+
178
  (* ---- the code of descriptions ----------------------------------------- *)
179
  (* kind 0 AND, 1 OR, 2 COPY; s0, s1 the polarities of the two literals. *)
180
  Definition gate_bits (kind : nat) (s0 s1 : bool) : list bool :=
rocq/extract/dump.ml CHANGED
@@ -1,14 +1,14 @@
1
  (* writes the netlist defined in Phys.v, extracted by Extract.v, in the layout of the netlist file of
2
- src/twohead_net.py: dump N L PATH *)
3
  let () =
4
- let n = int_of_string Sys.argv.(1) and l = int_of_string Sys.argv.(2) in
5
- let us = Physnet.phys_units n l and nx = Physnet.phys_next n l in
6
- let oc = open_out_bin Sys.argv.(3) in
7
  let put x = (* little-endian int32 *)
8
  let b = Bytes.create 4 in
9
  Bytes.set_int32_le b 0 (Int32.of_int x); output_bytes oc b in
10
  let edges = List.fold_left (fun a (u : Physnet.tu) -> a + List.length u.tin) 0 us in
11
- put (Physnet.nst n l); put (List.length us); put edges;
12
  List.iter (fun (u : Physnet.tu) -> put u.tb) us;
13
  List.iter (fun (u : Physnet.tu) -> put (List.length u.tin)) us;
14
  List.iter (fun (u : Physnet.tu) -> List.iter (fun (s, wt) -> put (wt * (s + 1))) u.tin) us;
 
1
  (* writes the netlist defined in Phys.v, extracted by Extract.v, in the layout of the netlist file of
2
+ src/twohead_net.py, for a window of 2^AL cells: dump AL N L PATH *)
3
  let () =
4
+ let al = int_of_string Sys.argv.(1) and n = int_of_string Sys.argv.(2) and l = int_of_string Sys.argv.(3) in
5
+ let us = Physnet.phys_units al n l and nx = Physnet.phys_next al n l in
6
+ let oc = open_out_bin Sys.argv.(4) in
7
  let put x = (* little-endian int32 *)
8
  let b = Bytes.create 4 in
9
  Bytes.set_int32_le b 0 (Int32.of_int x); output_bytes oc b in
10
  let edges = List.fold_left (fun a (u : Physnet.tu) -> a + List.length u.tin) 0 us in
11
+ put (Physnet.nst al n l); put (List.length us); put edges;
12
  List.iter (fun (u : Physnet.tu) -> put u.tb) us;
13
  List.iter (fun (u : Physnet.tu) -> put (List.length u.tin)) us;
14
  List.iter (fun (u : Physnet.tu) -> List.iter (fun (s, wt) -> put (wt * (s + 1))) u.tin) us;
src/check_interp.py DELETED
@@ -1,117 +0,0 @@
1
- """The one-record semantics of the interpreter, on its netlist.
2
-
3
- For the member U of the paper the netlist, followed by the output clause that
4
- the runtime applies, is compared with the reference of Definition 9.1 and its
5
- device; the pointer clause is part of the netlist:
6
-
7
- fields every weight pair, every bias and both input values of a record
8
- evaluated at record counter 0, 576 configurations;
9
- random uniformly random full states, stored records, pointer, PTR_ADV
10
- and record counter included, with the halt bit clear;
11
- halted uniformly random states with the halt bit set are fixed points;
12
- counter the record counter advances modulo G from every value.
13
-
14
- python src/check_interp.py [--states N] [--device cuda]
15
- """
16
- import argparse
17
- import json
18
- import os
19
- import sys
20
- import time
21
-
22
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
23
- import torch
24
- from reflect import (Cfg, SparseLeveled, apply_output, build_net, encode_netlist,
25
- ref_step, state_to_vec, vec_to_state)
26
-
27
- REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
28
-
29
-
30
- def runs_path(name: str) -> str:
31
- d = os.path.join(REPO, "paper", "runs")
32
- os.makedirs(d, exist_ok=True)
33
- return os.path.join(d, name)
34
-
35
-
36
- def check_member(cfg, n_states, device, seed):
37
- t0 = time.perf_counter()
38
- net, inputs, outputs = build_net(cfg)
39
- lev = SparseLeveled(net, inputs, outputs, device=device)
40
- res = {"A": cfg.A, "S": cfg.S, "G": cfg.G, "banks": cfg.banks,
41
- "units": len(net.gates)}
42
-
43
- def compare(states):
44
- X = torch.stack([state_to_vec(cfg, st) for st in states]).to(device)
45
- Y = lev.step(X).cpu()
46
- bad = 0
47
- for st, y in zip(states, Y):
48
- e = ref_step(cfg, st)
49
- g = vec_to_state(cfg, y)
50
- if st["halt"] == 0: # the output clause, applied by the runtime
51
- apply_output(cfg, g["sig"])
52
- if (g["sig"], g["gp"], g["halt"]) != (e["sig"], e["gp"], e["halt"]):
53
- bad += 1
54
- return bad
55
-
56
- ra, rb, ro = cfg.WORK_BASE, cfg.WORK_BASE + 1, cfg.WORK_BASE + 2
57
- states = []
58
- for w0 in (-1, 0, 1):
59
- for w1 in (-1, 0, 1):
60
- for bias in range(-(1 << (cfg.BB - 1)), 1 << (cfg.BB - 1)):
61
- nl = encode_netlist(cfg, [([(ra, w0, 0), (rb, w1, 0)], bias, (ro, 0))])
62
- for va in (0, 1):
63
- for vb in (0, 1):
64
- sig = [0] * cfg.S
65
- sig[cfg.NET0:cfg.NET0 + len(nl)] = nl
66
- sig[ra], sig[rb] = va, vb
67
- states.append({"sig": sig, "gp": 0, "halt": 0})
68
- res["fields"] = len(states)
69
- res["fields_wrong"] = compare(states)
70
-
71
- gen = torch.Generator().manual_seed(seed)
72
- wrong = 0
73
- for lo in range(0, n_states, 256):
74
- k = min(256, n_states - lo)
75
- sig = (torch.rand(k, cfg.S, generator=gen) < 0.5).long().tolist()
76
- gp = torch.randint(0, cfg.G, (k,), generator=gen).tolist()
77
- wrong += compare([{"sig": s, "gp": g, "halt": 0} for s, g in zip(sig, gp)])
78
- res["random"] = n_states
79
- res["random_wrong"] = wrong
80
-
81
- sig = (torch.rand(256, cfg.S, generator=gen) < 0.5).float()
82
- gp = torch.randint(0, cfg.G, (256,), generator=gen)
83
- X = torch.stack([state_to_vec(cfg, {"sig": s.long().tolist(), "gp": int(g), "halt": 1})
84
- for s, g in zip(sig, gp)]).to(device)
85
- res["halted_not_fixed"] = int((lev.step(X) != X).any(dim=1).sum())
86
-
87
- cbad = 0
88
- for g in range(cfg.G):
89
- st = {"sig": [0] * cfg.S, "gp": g, "halt": 0}
90
- y = vec_to_state(cfg, lev.step(state_to_vec(cfg, st).unsqueeze(0).to(device))[0].cpu())
91
- cbad += y["gp"] != (g + 1) % cfg.G
92
- res["counter_wrong"] = cbad
93
- res["ok"] = (res["fields_wrong"] == 0 and wrong == 0 and res["halted_not_fixed"] == 0
94
- and cbad == 0)
95
- res["seconds"] = time.perf_counter() - t0
96
- print(f" A = {cfg.A}, S = {cfg.S}, G = {cfg.G}, {res['units']:,} units: "
97
- f"{res['fields']} record configurations, {res['fields_wrong']} wrong; "
98
- f"{n_states:,} random states, {wrong} wrong; halted states fixed: "
99
- f"{'yes' if res['halted_not_fixed'] == 0 else 'NO'}; counter modulo G: "
100
- f"{'yes' if cbad == 0 else 'NO'} ({res['seconds']:.0f} s)", flush=True)
101
- return res
102
-
103
-
104
- def main() -> int:
105
- ap = argparse.ArgumentParser()
106
- ap.add_argument("--states", type=int, default=4096)
107
- ap.add_argument("--device", default="cuda" if torch.cuda.is_available() else "cpu")
108
- args = ap.parse_args()
109
- out = [check_member(Cfg(), args.states, args.device, 1)]
110
- ok = all(r["ok"] for r in out)
111
- json.dump({"members": out, "ok": ok}, open(runs_path("paper_interp.json"), "w"), indent=1)
112
- print("INTERPRETER:", "PASS" if ok else "FAIL")
113
- return 0 if ok else 1
114
-
115
-
116
- if __name__ == "__main__":
117
- sys.exit(main())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/check_lev.py CHANGED
@@ -106,30 +106,13 @@ def main() -> int:
106
  f"{len(D.W)} layers: {worst:.6f}")
107
  print(f" maximum distance of a pre-activation from an integer: {frac:.3e}")
108
 
109
- # The same check for the interpreter of Construction 9.2, whose levelization
110
- # Section 9 executes.
111
- from reflect import Cfg, build_net, Leveled
112
- cfg = Cfg()
113
- unet, uin, uout = build_net(cfg)
114
- UL = Leveled(unet, uin, uout, device="cpu")
115
- BU = 64
116
- VU = (torch.rand(BU, len(uin), generator=gen) < 0.5).float()
117
- udirect = gate_by_gate(unet, uin, uout, VU)
118
- ulev = UL.step(VU)
119
- uagree = bool((udirect == ulev).all())
120
- print(f" interpreter netlist has {len(unet.gates):,} gates on "
121
- f"{len(uin):,} state bits")
122
- print(f" gate-by-gate evaluation equals Lev(N_U) on {BU} uniformly random "
123
- f"states: {'yes' if uagree else 'NO'}")
124
-
125
- ok = (agree and agree_net and uagree and same_dense and not nonternary
126
  and not nonint and abs(worst - 0.5) < 1e-9)
127
  json.dump({"states": B, "gates": len(net.gates), "agree": agree,
128
  "agree_grouped": agree_net,
129
  "dense_agree": same_dense, "nonternary_layers": len(nonternary),
130
  "noninteger_bias_layers": len(nonint),
131
- "min_margin": worst, "max_noninteger": frac,
132
- "u_states": BU, "u_gates": len(unet.gates), "u_agree": uagree},
133
  open(runs_path("paper_lev.json"), "w"), indent=1)
134
  return 0 if ok else 1
135
 
 
106
  f"{len(D.W)} layers: {worst:.6f}")
107
  print(f" maximum distance of a pre-activation from an integer: {frac:.3e}")
108
 
109
+ ok = (agree and agree_net and same_dense and not nonternary
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  and not nonint and abs(worst - 0.5) < 1e-9)
111
  json.dump({"states": B, "gates": len(net.gates), "agree": agree,
112
  "agree_grouped": agree_net,
113
  "dense_agree": same_dense, "nonternary_layers": len(nonternary),
114
  "noninteger_bias_layers": len(nonint),
115
+ "min_margin": worst, "max_noninteger": frac},
 
116
  open(runs_path("paper_lev.json"), "w"), indent=1)
117
  return 0 if ok else 1
118
 
src/fanin2.py DELETED
@@ -1,220 +0,0 @@
1
- """Bringing a ternary threshold netlist to effective fan-in at most two.
2
-
3
- The interpreter evaluates records with two input slots, so a netlist it is to
4
- interpret must have units of fan-in at most two. Every ternary unit is a
5
- threshold over its literals: H(sum_P x - sum_N x + b) is 1 exactly when at
6
- least t = |N| - b of the literals x_i (i in P) and not x_i (i in N) are true.
7
- A disjunction (t = 1) or a conjunction (t = k) of k > 2 literals becomes a
8
- balanced binary tree of k - 1 two-input units, a literal carried by the sign
9
- of its weight and never by a unit of its own, so a leaf unit is a conjunction
10
- or disjunction of two literals and every inner unit one of two signals. Any
11
- other threshold, which in the host is a majority of three literals or two of
12
- four, follows the recursion T(t, x_1..x_k) = T(t, x_2..x_k) or (x_1 and
13
- T(t-1, x_2..x_k)), four units for a majority of three and eight for two of
14
- four. The rewriting preserves the computed function exactly, which is checked
15
- against the original netlist on random inputs.
16
- """
17
- from __future__ import annotations
18
-
19
- import os
20
- import sys
21
- from typing import Dict, List, Tuple
22
-
23
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
24
- from matrix8 import Net
25
-
26
-
27
- def classify(ins: List[Tuple[str, int]], bias: int) -> str:
28
- """Which shape a unit has, by its weights and bias: a disjunction of
29
- literals, a conjunction of literals, or a threshold of t of its k literals."""
30
- pos = [s for s, w in ins if w == 1]
31
- neg = [s for s, w in ins if w == -1]
32
- if len(pos) + len(neg) != len(ins):
33
- return "other"
34
- t = len(neg) - bias
35
- if t == 1:
36
- return "or"
37
- if t == len(ins):
38
- return "and" # conjunction of literals
39
- return "threshold"
40
-
41
-
42
- def threshold_of(ins: List[Tuple[str, int]], bias: int) -> int:
43
- """The number t of true literals the unit requires."""
44
- return sum(1 for _, w in ins if w == -1) - bias
45
-
46
-
47
- def _tree(net: Net, tag: str, kind: str, items: List[Tuple[str, int]],
48
- counter: List[int] = None) -> str:
49
- """A balanced binary tree of k - 1 two-input units over k literals.
50
-
51
- `items` are (signal, weight) pairs; weight -1 marks a negated literal,
52
- which a unit reads with weight -1, so negation costs no unit: a
53
- disjunction of two literals is H(+-a +-b + |N| - 1) and a conjunction
54
- H(+-a +-b - |P|). Every unit of the tree outputs a positive signal, so
55
- the levels above the leaves combine signals.
56
- """
57
- level = list(items)
58
- counter = counter if counter is not None else [0]
59
- while len(level) > 1:
60
- nxt = []
61
- for i in range(0, len(level) - 1, 2):
62
- (a, wa), (b, wb) = level[i], level[i + 1]
63
- name = f"{tag}#{kind}{counter[0]}"
64
- counter[0] += 1
65
- pos = (wa == 1) + (wb == 1)
66
- if kind == "or":
67
- nxt.append((net.g(name, [(a, wa), (b, wb)], (2 - pos) - 1), 1))
68
- else:
69
- nxt.append((net.g(name, [(a, wa), (b, wb)], -pos), 1))
70
- if len(level) % 2:
71
- nxt.append(level[-1])
72
- level = nxt
73
- s, w = level[0]
74
- if w != 1: # a single negated literal: one unit
75
- name = f"{tag}#{kind}{counter[0]}"
76
- counter[0] += 1
77
- return net.g(name, [(s, -1)], 0)
78
- return s
79
-
80
-
81
- def _thresh(net: Net, tag: str, t: int, items: List[Tuple[str, int]],
82
- counter: List[int]) -> Tuple[str, int]:
83
- """t of the k literals, by T(t, x_1..x_k) = T(t, rest) or (x_1 and T(t-1, rest)).
84
-
85
- Returns a (signal, weight) pair: a positive signal, or a constant."""
86
- k = len(items)
87
- if t <= 0:
88
- return ("#1", 1)
89
- if t > k:
90
- return ("#0", 1)
91
- if k == 1:
92
- return items[0]
93
- if t == 1:
94
- return (_tree(net, tag, "or", items, counter), 1)
95
- if t == k:
96
- return (_tree(net, tag, "and", items, counter), 1)
97
- x, rest = items[0], items[1:]
98
- a = _thresh(net, tag, t, rest, counter)
99
- b = _thresh(net, tag, t - 1, rest, counter)
100
- name = f"{tag}#and{counter[0]}"
101
- counter[0] += 1
102
- c = (net.g(name, [x, b], -((x[1] == 1) + 1)), 1)
103
- name = f"{tag}#or{counter[0]}"
104
- counter[0] += 1
105
- return (net.g(name, [a, c], -1), 1)
106
-
107
-
108
- def expand(net: Net, inputs: List[str], outputs: List[str]
109
- ) -> Tuple[Net, List[str], List[str]]:
110
- """An equivalent netlist whose units have effective fan-in at most two."""
111
- out = Net()
112
- sub: Dict[str, str] = {s: s for s in inputs}
113
- sub["#0"] = "#0"
114
- sub["#1"] = "#1"
115
-
116
- gates = net.gates
117
- indeg, cons = {}, {}
118
- for g, (ins, _) in gates.items():
119
- indeg[g] = len([s for s, _ in ins if s in gates])
120
- for s, _ in ins:
121
- cons.setdefault(s, []).append(g)
122
- order = [g for g, d in indeg.items() if d == 0]
123
- i = 0
124
- while i < len(order):
125
- for c in cons.get(order[i], []):
126
- indeg[c] -= 1
127
- if indeg[c] == 0:
128
- order.append(c)
129
- i += 1
130
- if len(order) != len(gates):
131
- raise ValueError("cycle")
132
-
133
- for g in order:
134
- ins, bias = gates[g]
135
- kind = classify(ins, bias)
136
- src = [(sub[s], w) for s, w in ins]
137
- if len(src) <= 2: # already within the slot budget
138
- sub[g] = out.g(g, src, bias)
139
- continue
140
- if kind in ("or", "and"):
141
- sub[g] = _tree(out, g, kind, src)
142
- elif kind == "threshold":
143
- assert len(src) <= 6, f"unit {g}: a threshold of {len(src)} literals"
144
- sig, w = _thresh(out, g, threshold_of(ins, bias), src, [0])
145
- assert w == 1
146
- sub[g] = sig
147
- else:
148
- raise ValueError(f"unit {g} carries a weight outside {{-1, 0, 1}}: "
149
- f"{src[:4]} bias {bias}")
150
- return out, list(inputs), [sub[s] for s in outputs]
151
-
152
-
153
- def check(net, inputs, outputs, trials: int = 64, seed: int = 5) -> bool:
154
- """The expansion computes the same function as the original netlist."""
155
- import torch
156
- from check_lev import gate_by_gate
157
- two, tin, tout = expand(net, inputs, outputs)
158
- fan = max(len(ins) for ins, _ in two.gates.values())
159
- assert fan <= 2, fan
160
- gen = torch.Generator().manual_seed(seed)
161
- V = (torch.rand(trials, len(inputs), generator=gen) < 0.5).float()
162
- a = gate_by_gate(net, inputs, outputs, V)
163
- b = gate_by_gate(two, tin, tout, V)
164
- return bool((a == b).all())
165
-
166
-
167
- def main() -> int:
168
- """The rewriting on the host and the interpreter, and on every ternary unit
169
- of three to five inputs with every bias, against the truth tables."""
170
- import itertools
171
- import json
172
- import time
173
- import torch
174
- from check_lev import gate_by_gate
175
- from host_family import build
176
- from reflect import Cfg, build_net
177
- REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
178
- t0 = time.perf_counter()
179
- tot = bad = 0
180
- for k in (3, 4, 5):
181
- names = [f"i{j}" for j in range(k)]
182
- V = torch.tensor(list(itertools.product((0.0, 1.0), repeat=k)))
183
- for signs in itertools.product((1, -1), repeat=k):
184
- for bias in range(-k - 1, k + 2):
185
- n1 = Net()
186
- n1.g("u", [(names[j], signs[j]) for j in range(k)], bias)
187
- e, _, o = expand(n1, names, ["u"])
188
- a = gate_by_gate(n1, names, ["u"], V)
189
- if o[0] in ("#0", "#1"):
190
- b = torch.full_like(a, 1.0 if o[0] == "#1" else 0.0)
191
- else:
192
- b = gate_by_gate(e, names, o, V)
193
- tot += 1
194
- bad += not bool((a == b).all())
195
- print(f" every ternary unit of 3 to 5 inputs with every bias, {tot} units, against "
196
- f"its truth table: {'all exact' if bad == 0 else f'{bad} FAILED'}")
197
- rec = {"small_units": tot, "small_unit_failures": bad, "netlists": {}}
198
- for label, (net, inp, out) in (("N_host", build(8)), ("N_U", build_net(Cfg()))):
199
- two, _, _ = expand(net, inp, out)
200
- ok = check(net, inp, out, trials=32)
201
- shapes = {}
202
- for ins, b in net.gates.values():
203
- if len(ins) > 2:
204
- shapes[classify(ins, b)] = shapes.get(classify(ins, b), 0) + 1
205
- rec["netlists"][label] = {"units": len(net.gates), "units_fanin2": len(two.gates),
206
- "shapes_above_two": shapes, "agrees_on_random_inputs": ok}
207
- bad += not ok
208
- print(f" {label}: {len(net.gates):,} units become {len(two.gates):,}; shapes above "
209
- f"fan-in two {shapes}; agrees with the original on 32 random states: {ok}")
210
- rec["seconds"] = time.perf_counter() - t0
211
- rec["ok"] = bad == 0
212
- d = os.path.join(REPO, "paper", "runs")
213
- os.makedirs(d, exist_ok=True)
214
- json.dump(rec, open(os.path.join(d, "paper_fanin2.json"), "w"), indent=1)
215
- print("FAN-IN TWO:", "PASS" if bad == 0 else "FAIL")
216
- return 0 if bad == 0 else 1
217
-
218
-
219
- if __name__ == "__main__":
220
- sys.exit(main())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/hierarchy.py DELETED
@@ -1,148 +0,0 @@
1
- """The interpreters form a hierarchy: a larger one runs a smaller one.
2
-
3
- The size proposition rules out U interpreting a netlist realizing itself. It
4
- does not rule out one member of the family running another, and that is what
5
- is established here. Writing U_S for the interpreter on S signals, a netlist
6
- realizing U_S is brought to effective fan-in two and laid out as records of
7
- U_{S'} in topological order, followed by one record per state coordinate that
8
- copies the next value back onto the coordinate. One pass of U_{S'} is then one
9
- transition of U_S carried out in place, and k passes are k transitions: the
10
- larger interpreter runs the smaller one, stored netlist and all, on every
11
- state tested.
12
-
13
- python src/hierarchy.py [--transitions K] [--device cuda]
14
- """
15
- import argparse
16
- import json
17
- import os
18
- import sys
19
- import time
20
-
21
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
22
- import torch
23
- from fanin2 import expand
24
- from reflect import (Cfg, Leveled, SparseLeveled, build_net, compile_to_reflect,
25
- encode_netlist, state_to_vec)
26
-
27
- REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
28
-
29
- SMALL = dict(A=7, G=3, banks=1, self_mod=False)
30
-
31
-
32
- def runs_path(name: str) -> str:
33
- d = os.path.join(REPO, "paper", "runs")
34
- os.makedirs(d, exist_ok=True)
35
- return os.path.join(d, name)
36
-
37
-
38
- def host_config(n2: int, signals: int, slack: int = 64):
39
- """The smallest A' whose interpreter has room for n2 records and the
40
- signals of the interpreted netlist, with G' chosen to make the addressed
41
- span as small as the layout allows."""
42
- for Ap in range(8, 28):
43
- Sp = 1 << Ap
44
- Rp = 3 * Ap + 11
45
- Gp = (Sp - signals - slack) // Rp
46
- if Gp >= n2:
47
- return Cfg(A=Ap, G=Gp, banks=1, self_mod=False)
48
- raise ValueError("no configuration is large enough")
49
-
50
-
51
- def main() -> int:
52
- ap = argparse.ArgumentParser()
53
- ap.add_argument("--transitions", type=int, default=4)
54
- ap.add_argument("--device", default="cuda" if torch.cuda.is_available() else "cpu")
55
- args = ap.parse_args()
56
- t_start = time.perf_counter()
57
- small = Cfg(**SMALL)
58
- net, inputs, outputs = build_net(small)
59
- two, tin, tout = expand(net, inputs, outputs)
60
- n2 = len(two.gates)
61
- signals = len(inputs) + n2
62
- # coordinates whose next value is a different signal need a copy record;
63
- # a coordinate the netlist passes through unchanged needs none
64
- moved = [(s, t) for s, t in zip(inputs, tout) if t != s]
65
- assert all(t in two.gates for _, t in moved), "a next value is another input"
66
- print(f" U_S: S = {small.S}, G = {small.G}, {len(net.gates):,} units; "
67
- f"at fan-in two {n2:,} units on {signals:,} signals; "
68
- f"{len(moved)} of {len(inputs)} coordinates change", flush=True)
69
-
70
- big = host_config(n2 + len(moved), signals)
71
- print(f" U_S': S' = {big.S}, G' = {big.G}, b' = {big.R}, addressed span "
72
- f"{big.span}; a record region of {big.G * big.R:,} bits", flush=True)
73
-
74
- base = big.WORK_BASE
75
- in_addr = {s: base + i for i, s in enumerate(inputs)}
76
- rg, addr, top = compile_to_reflect(big, two, in_addr, tout,
77
- base + len(inputs))
78
- assert len(rg) == n2
79
- assert top <= big.span, (top, big.span)
80
- copies = [([(addr[t], 1, 0)], -1, (in_addr[s], 0)) for s, t in moved]
81
- rg = rg + copies
82
- assert len(rg) <= big.G
83
- print(f" compiled to {n2:,} records and {len(copies)} copy records; "
84
- f"highest signal used {top}, span {big.span}", flush=True)
85
-
86
- t0 = time.perf_counter()
87
- bnet, bin_, bout = build_net(big)
88
- print(f" U_S' netlist: {len(bnet.gates):,} units "
89
- f"({time.perf_counter() - t0:.0f}s)", flush=True)
90
- t0 = time.perf_counter()
91
- lev = SparseLeveled(bnet, bin_, bout, device=args.device)
92
- print(f" levelized into {len(lev.plan)} layers "
93
- f"({time.perf_counter() - t0:.0f}s)", flush=True)
94
-
95
- nl = encode_netlist(big, rg)
96
- sig0 = [0] * big.S
97
- sig0[big.NET0:big.NET0 + len(nl)] = nl
98
- base_vec = state_to_vec(big, {"sig": sig0, "gp": 0, "halt": 0})
99
-
100
- # The states of U_S: random signal vectors, stored records included, a
101
- # random record counter, and the halt bit clear.
102
- B = 48
103
- gen = torch.Generator().manual_seed(20260910)
104
- small_lev = Leveled(net, inputs, outputs, device="cpu")
105
- V = (torch.rand(B, len(inputs), generator=gen) < 0.5).float()
106
- V[:, small.S + small.GPW] = 0.0 # halt bit clear
107
- V[:, :small.S][:, small.HALT_SIG] = 0.0 # and the halt signal
108
- X = base_vec.unsqueeze(0).repeat(B, 1)
109
- for i, s in enumerate(inputs):
110
- X[:, in_addr[s]] = V[:, i]
111
- X = X.to(args.device)
112
- in_idx = torch.tensor([in_addr[s] for s in inputs], device=args.device)
113
- t0 = time.perf_counter()
114
- exact = True
115
- wrong = 0
116
- per = []
117
- for k in range(args.transitions):
118
- V = small_lev.step(V)
119
- for _ in range(big.G):
120
- X = lev.step(X)
121
- got = X[:, in_idx].cpu()
122
- w = int((got != V).sum())
123
- per.append(w)
124
- wrong += w
125
- exact &= w == 0
126
- print(f" transition {k + 1}: U_S' state after {k + 1} passes "
127
- f"{'equals' if w == 0 else 'DIFFERS FROM'} U_S after {k + 1} "
128
- f"transitions on {B} random states "
129
- f"({(time.perf_counter() - t0) / 60:.1f} min)", flush=True)
130
- dt = time.perf_counter() - t0
131
- out = {"small": {"S": small.S, "G": small.G, "units": len(net.gates),
132
- "units_fanin2": n2, "signals": signals,
133
- "state_bits": len(inputs)},
134
- "big": {"S": big.S, "G": big.G, "b": big.R, "span": big.span,
135
- "units": len(bnet.gates), "layers": len(lev.plan),
136
- "Q_bits": big.G * big.R},
137
- "records": len(rg), "copy_records": len(copies), "states": B,
138
- "pass_steps": big.G, "transitions": args.transitions,
139
- "mismatches_per_transition": per,
140
- "exact": exact, "mismatched_coordinates": wrong,
141
- "seconds": time.perf_counter() - t_start}
142
- json.dump(out, open(runs_path("paper_hierarchy.json"), "w"), indent=1)
143
- print("HIERARCHY:", "PASS" if exact else "FAIL")
144
- return 0 if exact else 1
145
-
146
-
147
- if __name__ == "__main__":
148
- sys.exit(main())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/hosted_constructor.py DELETED
@@ -1,653 +0,0 @@
1
- """The universal constructor as a netlist stored in the interpreter's state.
2
-
3
- Section 9 of the paper interprets stored netlists; this module stores the
4
- paper's own construction. A microprogram of records implements the SUBLEQ
5
- transducer of Definitions 2.8 and 3.1 over a 128-byte memory, with the tape,
6
- the output word, the head and the write pointer all held in signals of the
7
- interpreter, and runs the constructor P on a recipe. One pass of the record
8
- region is one SUBLEQ step: fetch, subtract, write back, apply the device,
9
- branch. Nothing outside U takes part, not even U's own output device.
10
-
11
- The program is P with its variable and device cells moved from the top of a
12
- 256-byte memory to the top of a 128-byte one, which is the same program: every
13
- address it names is a datum, and the branch targets are unchanged.
14
-
15
- python src/hosted_constructor.py [--target-bytes N]
16
- """
17
- from __future__ import annotations
18
-
19
- import argparse
20
- import json
21
- import os
22
- import sys
23
- import time
24
- from typing import Dict, List, Tuple
25
-
26
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
27
- import torch
28
- from reflect import (Cfg, Leveled, SparseLeveled, build_net, encode_netlist,
29
- state_to_vec, to_bits)
30
- from selfrep import P, describe, decode
31
-
32
- REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
33
-
34
- MEM_BYTES = 128
35
- ADDR = 7 # address bits of the hosted machine
36
-
37
-
38
- def remap(x: int) -> int:
39
- """Move a cell of the 256-byte memory to the same offset from the top of a
40
- 128-byte one."""
41
- return x - 0x80 if x >= 0xF0 else x
42
-
43
-
44
- P128 = [(remap(a), remap(b), remap(c)) for a, b, c in P]
45
- Z, ONE, T1, T2, NEG1, EOK = 0x70, 0x71, 0x72, 0x73, 0x74, 0x75
46
- C_WR, C_EOT, C_RW, C_RD, R_IN, R_OUT = 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E
47
- HALT_PC = 0x7F
48
-
49
-
50
- def image128() -> List[int]:
51
- mem = [0] * MEM_BYTES
52
- for idx, (a, b, c) in enumerate(P128):
53
- mem[idx * 3] = a
54
- mem[idx * 3 + 1] = b
55
- mem[idx * 3 + 2] = c
56
- mem[ONE] = 1
57
- mem[NEG1] = 0xFF
58
- mem[EOK] = 1
59
- return mem
60
-
61
-
62
- def reference128(mem0: List[int], tape: bytes, max_steps: int = 1 << 22):
63
- """The transducer of Definitions 2.8 and 3.1 on a 128-byte memory."""
64
- mem = list(mem0)
65
- out = bytearray()
66
- h = 0
67
- pc = 0
68
- n = 0
69
- while pc != HALT_PC and n < max_steps:
70
- A = mem[pc % MEM_BYTES]
71
- B = mem[(pc + 1) % MEM_BYTES]
72
- C = mem[(pc + 2) % MEM_BYTES]
73
- r = (mem[B % MEM_BYTES] - mem[A % MEM_BYTES]) & 0xFF
74
- mem[B % MEM_BYTES] = r
75
- pc = C % MEM_BYTES if (r == 0 or r >= 0x80) else (pc + 3) % MEM_BYTES
76
- if mem[C_WR] == 1:
77
- out.append(mem[R_OUT])
78
- mem[R_OUT] = 0
79
- if mem[C_RD] == 1:
80
- if h < len(tape):
81
- mem[R_IN] = tape[h]
82
- mem[C_EOT] = 0
83
- h += 1
84
- else:
85
- mem[C_EOT] = 1
86
- if mem[C_RW] == 1:
87
- h = 0
88
- mem[C_WR] = mem[C_RD] = mem[C_RW] = 0
89
- n += 1
90
- return bytes(out), n, mem, h
91
-
92
-
93
- # =============================================================================
94
- # the microprogram
95
- # =============================================================================
96
-
97
- class Micro:
98
- """Records, with work signals allocated as they are named."""
99
-
100
- def __init__(self, cfg, work_base: int, work_max: int):
101
- self.cfg = cfg
102
- self.recs: List[Tuple[List[Tuple[int, int, int]], int, Tuple[int, int]]] = []
103
- self.next = work_base
104
- self.limit = work_base + work_max
105
- self.named: Dict[str, int] = {}
106
-
107
- def sig(self, name: str = None) -> int:
108
- s = self.next
109
- self.next += 1
110
- if self.next > self.limit:
111
- raise ValueError("work signals exhausted")
112
- if name:
113
- self.named[name] = s
114
- return s
115
-
116
- def reg(self, name: str, width: int) -> List[int]:
117
- r = [self.sig() for _ in range(width)]
118
- self.named[name] = r[0]
119
- return r
120
-
121
- def emit(self, slots, bias, out):
122
- self.recs.append((slots, bias, out))
123
-
124
- # --- one-record primitives ------------------------------------------
125
- def const(self, dst, v):
126
- self.emit([], 0 if v else -1, (dst, 0))
127
-
128
- def copy(self, dst, src):
129
- self.emit([(src, 1, 0)], -1, (dst, 0))
130
-
131
- def not_(self, dst, src):
132
- self.emit([(src, -1, 0)], 0, (dst, 0))
133
-
134
- def and2(self, dst, a, b, na=False, nb=False):
135
- wa, wb = (-1 if na else 1), (-1 if nb else 1)
136
- bias = -((0 if na else 1) + (0 if nb else 1))
137
- self.emit([(a, wa, 0), (b, wb, 0)], bias, (dst, 0))
138
-
139
- def or2(self, dst, a, b):
140
- self.emit([(a, 1, 0), (b, 1, 0)], -1, (dst, 0))
141
-
142
- def read_idx(self, dst, base):
143
- """dst <- sig[base + PTR]."""
144
- self.emit([(base, 1, 1)], -1, (dst, 0))
145
-
146
- def write_idx(self, base, src):
147
- """sig[base + PTR] <- src."""
148
- self.emit([(src, 1, 0)], -1, (base, 1))
149
-
150
- def write_idx_and(self, base, src, gate, ngate=False):
151
- """sig[base + PTR] <- src AND gate (or src AND NOT gate)."""
152
- wg = -1 if ngate else 1
153
- bias = -1 if ngate else -2
154
- self.emit([(src, 1, 0), (gate, wg, 0)], bias, (base, 1))
155
-
156
- # --- small cells ------------------------------------------------------
157
- def xor(self, dst, a, b):
158
- t1, t2 = self.sig(), self.sig()
159
- self.or2(t1, a, b)
160
- self.emit([(a, -1, 0), (b, -1, 0)], 1, (t2, 0)) # NAND
161
- self.and2(dst, t1, t2)
162
-
163
- def mux(self, dst, sel, x1, x0):
164
- ns, a1, a0 = self.sig(), self.sig(), self.sig()
165
- self.not_(ns, sel)
166
- self.and2(a1, x1, sel)
167
- self.and2(a0, x0, ns)
168
- self.or2(dst, a1, a0)
169
-
170
- def and_lits(self, dst, lits):
171
- """dst <- conjunction of literals (signal, want) via a binary tree."""
172
- level = []
173
- i = 0
174
- while i < len(lits):
175
- if i + 1 < len(lits):
176
- (sa, wa), (sb, wb) = lits[i], lits[i + 1]
177
- t = self.sig()
178
- self.and2(t, sa, sb, na=not wa, nb=not wb)
179
- level.append(t)
180
- i += 2
181
- else:
182
- sa, wa = lits[i]
183
- t = self.sig()
184
- if wa:
185
- self.copy(t, sa)
186
- else:
187
- self.not_(t, sa)
188
- level.append(t)
189
- i += 1
190
- while len(level) > 1:
191
- nxt = []
192
- for j in range(0, len(level) - 1, 2):
193
- last = len(level) == 2
194
- t = dst if last else self.sig()
195
- self.and2(t, level[j], level[j + 1])
196
- nxt.append(t)
197
- if len(level) % 2:
198
- nxt.append(level[-1])
199
- level = nxt
200
- if level[0] != dst:
201
- self.copy(dst, level[0])
202
-
203
- def incr_gated(self, reg, gate):
204
- """reg <- reg + gate, on len(reg) bits, least significant first."""
205
- carries = [gate]
206
- for k in range(len(reg) - 1):
207
- c = self.sig()
208
- self.and2(c, reg[k], carries[k])
209
- carries.append(c)
210
- for k in range(len(reg)):
211
- self.xor(reg[k], reg[k], carries[k])
212
-
213
- def add_const(self, dst, src, c):
214
- """dst <- src + c on len(src) bits, least significant first."""
215
- carry = None
216
- for k in range(len(src)):
217
- bit = (c >> k) & 1
218
- if bit and carry is None:
219
- self.not_(dst[k], src[k])
220
- carry = src[k]
221
- elif bit:
222
- t = self.sig()
223
- self.xor(t, src[k], carry)
224
- self.not_(dst[k], t)
225
- nc = self.sig()
226
- self.or2(nc, src[k], carry)
227
- carry = nc
228
- elif carry is None:
229
- self.copy(dst[k], src[k])
230
- else:
231
- nc = self.sig()
232
- self.and2(nc, src[k], carry)
233
- self.xor(dst[k], src[k], carry)
234
- carry = nc
235
-
236
-
237
- def build_microprogram(cfg, mem_base, tape_base, out_base, tape_len,
238
- work_base, work_max):
239
- """The records implementing one SUBLEQ step with its device."""
240
- m = Micro(cfg, work_base, work_max)
241
- PC = m.reg("PC", ADDR) # least significant first
242
- P1 = m.reg("P1", ADDR)
243
- P2 = m.reg("P2", ADDR)
244
- P3 = m.reg("P3", ADDR)
245
- Ab = m.reg("A", 8)
246
- Bb = m.reg("B", 8)
247
- Cb = m.reg("C", 8)
248
- X = m.reg("X", 8)
249
- Y = m.reg("Y", 8)
250
- Rb = m.reg("R", 8)
251
- NPC = m.reg("NPC", ADDR)
252
- head = m.reg("head", ADDR)
253
- wp = m.reg("wp", ADDR)
254
-
255
- def cell(addr, bit):
256
- """The signal holding bit `bit` of memory cell `addr`."""
257
- return mem_base + bit * MEM_BYTES + addr
258
-
259
- def ptr_set(src):
260
- """PTR <- src, on ADDR bits; the pointer is stored most significant
261
- first, so bit k of the value is coordinate PTR_BASE + A - 1 - k."""
262
- for k in range(ADDR):
263
- m.copy(cfg.PTR_BASE + cfg.A - 1 - k, src[k])
264
-
265
- # the pointer's high bits stay zero
266
- for k in range(ADDR, cfg.A):
267
- m.const(cfg.PTR_BASE + cfg.A - 1 - k, 0)
268
-
269
- m.add_const(P1, PC, 1)
270
- m.add_const(P2, PC, 2)
271
- m.add_const(P3, PC, 3)
272
-
273
- for reg, src in ((Ab, PC), (Bb, P1), (Cb, P2)):
274
- ptr_set(src)
275
- for b in range(8):
276
- m.read_idx(reg[b], mem_base + b * MEM_BYTES)
277
- for reg, src in ((X, Ab), (Y, Bb)):
278
- ptr_set(src[:ADDR])
279
- for b in range(8):
280
- m.read_idx(reg[b], mem_base + b * MEM_BYTES)
281
-
282
- # R = Y - X, and the branch decision
283
- nx = [m.sig() for _ in range(8)]
284
- for b in range(8):
285
- m.not_(nx[b], X[b])
286
- carry = None
287
- for b in range(8):
288
- t = m.sig()
289
- if carry is None: # carry in is 1
290
- m.xor(t, Y[b], nx[b])
291
- m.not_(Rb[b], t)
292
- c = m.sig()
293
- m.or2(c, Y[b], nx[b])
294
- else:
295
- m.xor(t, Y[b], nx[b])
296
- m.xor(Rb[b], t, carry)
297
- c1, c2, c = m.sig(), m.sig(), m.sig()
298
- m.and2(c1, Y[b], nx[b])
299
- m.and2(c2, t, carry)
300
- m.or2(c, c1, c2)
301
- carry = c
302
- nz = m.sig()
303
- tree = Rb[0]
304
- for b in range(1, 8):
305
- t = m.sig()
306
- m.or2(t, tree, Rb[b])
307
- tree = t
308
- m.not_(nz, tree)
309
- leq = m.sig()
310
- m.or2(leq, Rb[7], nz)
311
-
312
- # write back to M[B]
313
- ptr_set(Bb[:ADDR])
314
- for b in range(8):
315
- m.write_idx(mem_base + b * MEM_BYTES, Rb[b])
316
-
317
- # --- the device ------------------------------------------------------
318
- wr, rd, rw = m.sig(), m.sig(), m.sig()
319
- m.and_lits(wr, [(cell(C_WR, 0), 1)] + [(cell(C_WR, b), 0) for b in range(1, 8)])
320
- m.and_lits(rd, [(cell(C_RD, 0), 1)] + [(cell(C_RD, b), 0) for b in range(1, 8)])
321
- m.and_lits(rw, [(cell(C_RW, 0), 1)] + [(cell(C_RW, b), 0) for b in range(1, 8)])
322
-
323
- # read: R_IN <- tape[head] when the head is inside the tape
324
- ateot = m.sig()
325
- m.and_lits(ateot, [(head[k], (tape_len >> k) & 1) for k in range(ADDR)])
326
- inrange = m.sig()
327
- m.not_(inrange, ateot)
328
- g = m.sig()
329
- m.and2(g, rd, inrange)
330
- ptr_set(head)
331
- tb = [m.sig() for _ in range(8)]
332
- for b in range(8):
333
- m.read_idx(tb[b], tape_base + b * tape_len)
334
- for b in range(8):
335
- m.mux(cell(R_IN, b), g, tb[b], cell(R_IN, b))
336
- # end-of-tape status: set by a read at the end, cleared by a read inside
337
- m.mux(cell(C_EOT, 0), rd, ateot, cell(C_EOT, 0))
338
- for b in range(1, 8):
339
- m.and2(cell(C_EOT, b), cell(C_EOT, b), rd, nb=True)
340
- m.incr_gated(head, g)
341
- for k in range(ADDR): # rewind sets the head to zero
342
- m.and2(head[k], head[k], rw, nb=True)
343
-
344
- # write: append R_OUT to the output word, then clear R_OUT
345
- ptr_set(wp)
346
- for b in range(8):
347
- m.write_idx_and(out_base + b * tape_len, cell(R_OUT, b), wr)
348
- for b in range(8):
349
- m.and2(cell(R_OUT, b), cell(R_OUT, b), wr, nb=True)
350
- m.incr_gated(wp, wr)
351
-
352
- # the request cells are cleared at the end of every step
353
- for b in range(8):
354
- m.const(cell(C_WR, b), 0)
355
- m.const(cell(C_RD, b), 0)
356
- m.const(cell(C_RW, b), 0)
357
-
358
- # --- branch and halt --------------------------------------------------
359
- for k in range(ADDR):
360
- m.mux(NPC[k], leq, Cb[k], P3[k])
361
- m.and_lits(cfg.HALT_SIG, [(NPC[k], (HALT_PC >> k) & 1) for k in range(ADDR)])
362
- for k in range(ADDR):
363
- m.copy(PC[k], NPC[k])
364
- return m
365
-
366
-
367
- def step128(mem, pc, tape, h, out, wp):
368
- """One step of the transducer of Definitions 2.8 and 3.1 on a 128-byte
369
- memory whose tape is the whole tape region: the SUBLEQ instruction, then
370
- the write, read, rewind and clear clauses of the device."""
371
- A = mem[pc % MEM_BYTES]
372
- B = mem[(pc + 1) % MEM_BYTES]
373
- C = mem[(pc + 2) % MEM_BYTES]
374
- r = (mem[B % MEM_BYTES] - mem[A % MEM_BYTES]) & 0xFF
375
- mem[B % MEM_BYTES] = r
376
- pc = C % MEM_BYTES if (r == 0 or r >= 0x80) else (pc + 3) % MEM_BYTES
377
- if mem[C_WR] == 1:
378
- out[wp] = mem[R_OUT]
379
- wp += 1
380
- mem[R_OUT] = 0
381
- if mem[C_RD] == 1:
382
- if h < len(tape):
383
- mem[R_IN] = tape[h]
384
- mem[C_EOT] = 0
385
- h += 1
386
- else:
387
- mem[C_EOT] = 1
388
- if mem[C_RW] == 1:
389
- h = 0
390
- mem[C_WR] = mem[C_RD] = mem[C_RW] = 0
391
- return pc, h, wp
392
-
393
-
394
- def random_pass_check(cfg, m, nl, mem_base, tape_base, out_base, tape_len,
395
- n_ref: int, n_net: int, device: str, seed: int = 11):
396
- """One pass of the microprogram against one step of the transducer, from
397
- random states: random memory, counter, tape, head, write pointer, output
398
- below the write pointer and scratch registers; the output region above the
399
- write pointer is clear, as it is along every run."""
400
- import random as _r
401
- rng = _r.Random(seed)
402
- regs = {k: m.named[k] for k in ("PC", "head", "wp")}
403
-
404
- def make():
405
- sig = [rng.randint(0, 1) for _ in range(cfg.S)]
406
- sig[cfg.NET0:cfg.NET0 + len(nl)] = nl
407
- for c in (cfg.PTR_ADV, cfg.OUT_STROBE, cfg.HALT_SIG, cfg.BANK_SIG):
408
- sig[c] = 0
409
- mem = [rng.randrange(256) for _ in range(MEM_BYTES)]
410
- tape = [rng.randrange(256) for _ in range(tape_len)]
411
- pc = rng.choice([x for x in range(MEM_BYTES) if x != HALT_PC])
412
- h = rng.randint(0, tape_len)
413
- wp = rng.randint(0, tape_len - 2)
414
- out = [rng.randrange(256) if j < wp else 0 for j in range(tape_len)]
415
- for j in range(MEM_BYTES):
416
- for b in range(8):
417
- sig[mem_base + b * MEM_BYTES + j] = (mem[j] >> b) & 1
418
- for j in range(tape_len):
419
- for b in range(8):
420
- sig[tape_base + b * tape_len + j] = (tape[j] >> b) & 1
421
- sig[out_base + b * tape_len + j] = (out[j] >> b) & 1
422
- for name, val in (("PC", pc), ("head", h), ("wp", wp)):
423
- for k in range(ADDR):
424
- sig[regs[name] + k] = (val >> k) & 1
425
- return sig, mem, tape, pc, h, out, wp
426
-
427
- def expect(mem, tape, pc, h, out, wp):
428
- mem, out = list(mem), list(out)
429
- pc, h, wp = step128(mem, pc, tape, h, out, wp)
430
- return mem, pc, h, out, wp
431
-
432
- def observe(sig):
433
- byte = lambda base, stride, j: sum(int(sig[base + b * stride + j]) << b for b in range(8))
434
- val = lambda name: sum(int(sig[regs[name] + k]) << k for k in range(ADDR))
435
- return ([byte(mem_base, MEM_BYTES, j) for j in range(MEM_BYTES)], val("PC"),
436
- val("head"), [byte(out_base, tape_len, j) for j in range(tape_len)],
437
- val("wp"), int(sig[cfg.HALT_SIG]))
438
-
439
- def agrees(got, e_mem, e_pc, e_h, e_out, e_wp):
440
- g_mem, g_pc, g_h, g_out, g_wp, g_halt = got
441
- if e_pc == HALT_PC:
442
- # the counter is copied after the halt signal is raised, and the
443
- # interpreter stops within the copy, so a halting step is judged
444
- # on everything but the counter
445
- return (g_mem, g_h, g_out, g_wp, g_halt) == (e_mem, e_h, e_out, e_wp, 1)
446
- return got == (e_mem, e_pc, e_h, e_out, e_wp, 0)
447
-
448
- bad_ref = 0
449
- for _ in range(n_ref):
450
- sig, mem, tape, pc, h, out, wp = make()
451
- e = expect(mem, tape, pc, h, out, wp)
452
- run_records(m.recs, sig, cfg, 1)
453
- if not agrees(observe(sig), *e):
454
- bad_ref += 1
455
- if n_net == 0:
456
- return bad_ref, 0
457
- states = [make() for _ in range(n_net)]
458
- lev = SparseLeveled(*build_net(cfg), device=device)
459
- X = torch.stack([state_to_vec(cfg, {"sig": st[0], "gp": 0, "halt": 0})
460
- for st in states]).to(device)
461
- for _ in range(cfg.G):
462
- X = lev.step(X)
463
- X = X.cpu()
464
- bad_net = 0
465
- for i, (sig, mem, tape, pc, h, out, wp) in enumerate(states):
466
- e = expect(mem, tape, pc, h, out, wp)
467
- if not agrees(observe(X[i].tolist()), *e):
468
- bad_net += 1
469
- return bad_ref, bad_net
470
-
471
-
472
- def run_records(recs, sig, cfg, max_passes):
473
- """Definition 9.1 applied record by record, in place.
474
-
475
- Identical in semantics to reflect.ref_gate, and used here so that the
476
- microprogram can be checked against the reference machine without
477
- evaluating the interpreter's netlist.
478
- """
479
- S, PB, A = cfg.S, cfg.PTR_BASE, cfg.A
480
- for p in range(max_passes):
481
- for slots, bias, (oa, oidx) in recs:
482
- ptr = 0
483
- for k in range(A):
484
- ptr = (ptr << 1) | sig[PB + k]
485
- acc = bias
486
- for (a, w, idx) in slots:
487
- acc += w * sig[(a + (ptr if idx else 0)) & (S - 1)]
488
- sig[(oa + (ptr if oidx else 0)) & (S - 1)] = 1 if acc >= 0 else 0
489
- if sig[cfg.HALT_SIG]:
490
- return p + 1, True
491
- return max_passes, False
492
-
493
-
494
- def layout(tape_len: int, out_len: int, work_max: int = 700,
495
- min_records: int = 1):
496
- """The smallest configuration with room for the microprogram and the
497
- regions it addresses."""
498
- for A in range(12, 22):
499
- S = 1 << A
500
- R = 3 * A + 11
501
- work_base = A + 6
502
- mem_base = work_base + work_max
503
- tape_base = mem_base + 8 * MEM_BYTES
504
- out_base = tape_base + 8 * tape_len
505
- top = out_base + 8 * out_len
506
- for G in range((S - top) // R, min_records - 1, -1):
507
- span = S - G * R
508
- if span >= top + 8:
509
- return A, G, span, mem_base, tape_base, out_base, work_base
510
- raise ValueError("no configuration is large enough")
511
-
512
-
513
- def main() -> int:
514
- ap = argparse.ArgumentParser()
515
- ap.add_argument("--target", default="ABCCCCCCCCDE")
516
- ap.add_argument("--device", default="cuda" if torch.cuda.is_available() else "cpu")
517
- ap.add_argument("--random-states", type=int, default=2000)
518
- ap.add_argument("--net-states", type=int, default=256)
519
- args = ap.parse_args()
520
-
521
- target = args.target.encode()
522
- tape = describe(target)
523
- assert decode(tape) == target
524
- tape_len = out_len = 64
525
- assert len(tape) < tape_len and len(target) < out_len
526
-
527
- A, G, span, mem_base, tape_base, out_base, work_base = layout(
528
- tape_len, out_len, min_records=520)
529
- cfg = Cfg(A=A, G=G, banks=1, self_mod=False)
530
- assert cfg.span == span, (cfg.span, span)
531
- m = build_microprogram(cfg, mem_base, tape_base, out_base, tape_len,
532
- work_base, 700)
533
- n = len(m.recs)
534
- assert n <= G, (n, G)
535
- print(f" hosted machine: SUBLEQ over {MEM_BYTES} bytes with the tape, the "
536
- f"output word, the head and the write pointer in signals", flush=True)
537
- print(f" microprogram: {n} records; interpreter U_S': S = {cfg.S}, "
538
- f"G = {cfg.G}, b = {cfg.R}, addressed span {cfg.span}", flush=True)
539
- print(f" regions: work {work_base}..{m.next - 1}, memory {mem_base}, "
540
- f"tape {tape_base}, output {out_base}", flush=True)
541
-
542
- exp_out, exp_steps, exp_mem, exp_head = reference128(image128(), tape)
543
- assert exp_out == target, (exp_out, target)
544
- print(f" the reference runs P on a {len(tape)}-byte recipe in "
545
- f"{exp_steps} instructions, emitting {len(exp_out)} bytes", flush=True)
546
-
547
- nl = encode_netlist(cfg, m.recs)
548
-
549
- def initial_signals():
550
- sig = [0] * cfg.S
551
- sig[cfg.NET0:cfg.NET0 + len(nl)] = nl
552
- mem0 = image128()
553
- for j in range(MEM_BYTES):
554
- for b in range(8):
555
- sig[mem_base + b * MEM_BYTES + j] = (mem0[j] >> b) & 1
556
- for j, byte in enumerate(tape):
557
- for b in range(8):
558
- sig[tape_base + b * tape_len + j] = (byte >> b) & 1
559
- return sig
560
-
561
- def readout(sig):
562
- def byte_at(base, stride, j):
563
- return sum(int(sig[base + b * stride + j]) << b for b in range(8))
564
- return (bytes(byte_at(out_base, tape_len, j) for j in range(len(target))),
565
- [byte_at(mem_base, MEM_BYTES, j) for j in range(MEM_BYTES)],
566
- sum(int(sig[m.named["wp"] + k]) << k for k in range(ADDR)),
567
- sum(int(sig[m.named["head"] + k]) << k for k in range(ADDR)))
568
-
569
- # first at the level of Definition 9.1, record by record
570
- t0 = time.perf_counter()
571
- rsig = initial_signals()
572
- rpasses, rhalted = run_records(m.recs, rsig, cfg, exp_steps + 3)
573
- r_out, r_mem, r_wp, r_head = readout(rsig)
574
- rec_ok = (rhalted and rpasses == exp_steps and r_out == target
575
- and r_mem == exp_mem and r_head == exp_head)
576
- print(f" record semantics: halted after {rpasses} passes, emitted "
577
- f"{r_wp} bytes {r_out!r}, memory and head match the reference: "
578
- f"{'yes' if rec_ok else 'NO'} ({time.perf_counter() - t0:.1f}s)",
579
- flush=True)
580
-
581
- t0 = time.perf_counter()
582
- unet, uin, uout = build_net(cfg)
583
- lev = Leveled(unet, uin, uout, device=args.device)
584
- print(f" U_S' netlist: {len(unet.gates):,} units, {len(lev.plan)} layers "
585
- f"({time.perf_counter() - t0:.0f}s)", flush=True)
586
-
587
- V = state_to_vec(cfg, {"sig": initial_signals(), "gp": 0, "halt": 0}
588
- ).unsqueeze(0).to(args.device)
589
-
590
- t0 = time.perf_counter()
591
- halted_at = None
592
- for inst in range(exp_steps + 2):
593
- for _ in range(cfg.G):
594
- V = lev.step(V)
595
- if int(V[0, cfg.S + cfg.GPW]) == 1:
596
- halted_at = inst + 1
597
- break
598
- if inst and inst % 20 == 0:
599
- print(f" {inst}/{exp_steps} instructions "
600
- f"({inst * cfg.G / (time.perf_counter() - t0):.0f} steps/s)",
601
- flush=True)
602
- dt = time.perf_counter() - t0
603
- got, got_mem, wp_val, head_val = readout(V[0].cpu())
604
-
605
- exact = got == target
606
- mem_same = got_mem == exp_mem
607
- print(f" ran to a halt after {halted_at} instructions "
608
- f"({halted_at * cfg.G:,} interpreter steps, {dt / 60:.1f} min, "
609
- f"{halted_at * cfg.G / dt:.0f} steps/s)", flush=True)
610
- print(f" emitted {wp_val} bytes: {got!r}")
611
- print(f" equals the target: {'yes' if exact else 'NO'}; final memory "
612
- f"equals the reference: {'yes' if mem_same else 'NO'}; head "
613
- f"{head_val} of {len(tape)}: "
614
- f"{'yes' if head_val == exp_head else 'NO'}")
615
-
616
- t0 = time.perf_counter()
617
- dev_rand = "cuda" if torch.cuda.is_available() else args.device
618
- bad_ref, bad_net = random_pass_check(cfg, m, nl, mem_base, tape_base, out_base,
619
- tape_len, args.random_states,
620
- args.net_states, dev_rand)
621
- print(f" one pass against one step of the transducer from random states: "
622
- f"{args.random_states:,} on the record semantics, "
623
- f"{'all exact' if bad_ref == 0 else f'{bad_ref} WRONG'}; "
624
- f"{args.net_states:,} on the netlist, "
625
- f"{'all exact' if bad_net == 0 else f'{bad_net} WRONG'} "
626
- f"({time.perf_counter() - t0:.0f}s)", flush=True)
627
-
628
- ok = (rec_ok and exact and mem_same and halted_at == exp_steps
629
- and head_val == exp_head and bad_ref == 0 and bad_net == 0)
630
- d = os.path.join(REPO, "paper", "runs")
631
- os.makedirs(d, exist_ok=True)
632
- json.dump({"records": n, "record_semantics_ok": rec_ok,
633
- "S": cfg.S, "G": cfg.G, "b": cfg.R,
634
- "span": cfg.span, "units": len(unet.gates),
635
- "layers": len(lev.plan), "memory_bytes": MEM_BYTES,
636
- "tape_bytes": len(tape), "target_bytes": len(target),
637
- "target": target.decode(), "instructions": halted_at,
638
- "reference_instructions": exp_steps,
639
- "interpreter_steps": halted_at * cfg.G,
640
- "emitted": got.decode(errors="replace"), "exact": exact,
641
- "memory_matches_reference": mem_same, "head": head_val,
642
- "random_states_records": args.random_states,
643
- "random_states_records_wrong": bad_ref,
644
- "random_states_netlist": args.net_states,
645
- "random_states_netlist_wrong": bad_net,
646
- "seconds": dt, "ok": ok},
647
- open(os.path.join(d, "paper_hosted.json"), "w"), indent=1)
648
- print("HOSTED CONSTRUCTOR:", "PASS" if ok else "FAIL")
649
- return 0 if ok else 1
650
-
651
-
652
- if __name__ == "__main__":
653
- sys.exit(main())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/independent/interpreter.py DELETED
@@ -1,495 +0,0 @@
1
- """Definition 9.1 transcribed afresh, and the two runs of Section 9 on it.
2
-
3
- python src/independent/interpreter.py
4
-
5
- The artifact supplies only the objects under test: the 494 stored records of
6
- Theorem 9.10 with the register addresses they use, and the netlist of U_128 that
7
- Proposition 9.8 stores. Record decoding, the interpreter, the fan-in-two
8
- compilation, the layout of Proposition 9.8, the SUBLEQ machine on 128 bytes, the
9
- device and the constructor P (from Table 9 of the paper) are written here.
10
- """
11
- import json
12
- import os
13
- import random
14
- import sys
15
- import time
16
-
17
- sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
18
- RUNS = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'paper', 'runs')
19
-
20
- # ---------------------------------------------------------------------------
21
- # Definition 9.1
22
- # ---------------------------------------------------------------------------
23
-
24
-
25
- class Member:
26
- def __init__(self, A, G, span, banks=1):
27
- self.A, self.G, self.span = A, G, span
28
- self.S = 1 << A
29
- self.b = 3 * A + 11
30
- self.OUT_DATA, self.OUT_STROBE, self.ADV, self.KAPPA, self.HALT = A, A + 1, A + 2, A + 3, A + 4
31
- self.Q0 = self.S - banks * G * self.b
32
-
33
-
34
- def decode_record(bits, A):
35
- """(a0, w0, i0, a1, w1, i1, beta, ao, io), addresses most significant bit first."""
36
- pos = 0
37
-
38
- def take(n):
39
- nonlocal pos
40
- v = 0
41
- for k in range(n):
42
- v = (v << 1) | bits[pos + k]
43
- pos += n
44
- return v
45
- wmap = {0b01: 1, 0b10: -1, 0b00: 0, 0b11: 0}
46
- a0 = take(A); w0 = wmap[take(2)]; i0 = take(1)
47
- a1 = take(A); w1 = wmap[take(2)]; i1 = take(1)
48
- beta = take(4); beta = beta - 16 if beta >= 8 else beta
49
- ao = take(A); io = take(1)
50
- assert pos == 3 * A + 11
51
- return a0, w0, i0, a1, w1, i1, beta, ao, io
52
-
53
-
54
- def encode_record(rec, A):
55
- a0, w0, i0, a1, w1, i1, beta, ao, io = rec
56
- out = []
57
- put = lambda v, n: out.extend((v >> (n - 1 - k)) & 1 for k in range(n))
58
- wc = {1: 0b01, -1: 0b10, 0: 0b00}
59
- put(a0, A); put(wc[w0], 2); put(i0, 1)
60
- put(a1, A); put(wc[w1], 2); put(i1, 1)
61
- put(beta & 15, 4); put(ao, A); put(io, 1)
62
- return out
63
-
64
-
65
- class Interp:
66
- """The interpreter on a state x of S coordinates, with record counter and halt bit."""
67
- def __init__(self, M, x):
68
- self.M, self.x, self.g, self.halt, self.out = M, x, 0, 0, []
69
- self.recs = [decode_record(x[M.Q0 + k * M.b: M.Q0 + (k + 1) * M.b], M.A) for k in range(M.G)]
70
-
71
- def ptr(self):
72
- v = 0
73
- for k in range(self.M.A):
74
- v = (v << 1) | self.x[k]
75
- return v
76
-
77
- def apply(self):
78
- M, x = self.M, self.x
79
- if self.halt:
80
- return
81
- a0, w0, i0, a1, w1, i1, beta, ao, io = self.recs[self.g]
82
- p = self.ptr()
83
- eff = lambda a, i: (a + i * p) % M.S
84
- rd = lambda a: x[a] if a < M.span else 0
85
- v = 1 if w0 * rd(eff(a0, i0)) + w1 * rd(eff(a1, i1)) + beta >= 0 else 0
86
- nhalt = self.halt | x[M.HALT]
87
- dst = eff(ao, io)
88
- if dst < M.span:
89
- x[dst] = v
90
- if x[M.ADV]: # the pointer clause
91
- q = (p + 1) % M.S
92
- for k in range(M.A):
93
- x[k] = (q >> (M.A - 1 - k)) & 1
94
- x[M.ADV] = 0
95
- if x[M.OUT_STROBE]: # the output clause, by the runtime
96
- self.out.append(x[M.OUT_DATA])
97
- x[M.OUT_STROBE] = 0
98
- self.g = (self.g + 1) % M.G
99
- self.halt = nhalt
100
-
101
-
102
- # ---------------------------------------------------------------------------
103
- # The hosted transducer: SUBLEQ on 128 bytes (Definition 3.1 with addresses
104
- # modulo 128, halting at 0x7F) and the device of Definition 2.8 at the top cells
105
- # ---------------------------------------------------------------------------
106
-
107
- Z, ONE, T1, T2, NEG1, EOK = 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5
108
- C_WR, C_EOT, C_RW, C_RD, R_IN, R_OUT = 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE
109
- TABLE9 = [ # Table 9 of the paper
110
- (T2, T2, 0x03), (T1, T1, 0x06), (NEG1, C_RD, 0x09), (R_IN, T1, 0x0C), (T1, T2, 0x0F),
111
- (Z, T2, 0x15), (Z, Z, 0x1B), (Z, T1, 0x3C), (Z, Z, 0x2D), (NEG1, C_RD, 0x1E),
112
- (R_IN, R_OUT, 0x21), (NEG1, C_WR, 0x24), (ONE, T2, 0x2A), (Z, Z, 0x1B), (Z, Z, 0x00),
113
- (NEG1, C_RD, 0x30), (R_IN, R_OUT, 0x33), (NEG1, C_WR, 0x36), (ONE, T1, 0x2A), (Z, Z, 0x30),
114
- (Z, Z, 0xFF)]
115
- mv = lambda c: c - 0x80 if c >= 0xF0 else c
116
-
117
-
118
- def p128_image():
119
- mem = [0] * 128
120
- for k, (a, b, c) in enumerate(TABLE9):
121
- mem[3 * k:3 * k + 3] = [mv(a), mv(b), mv(c)]
122
- mem[mv(ONE)] = 1
123
- mem[mv(NEG1)] = 255
124
- mem[mv(EOK)] = 1
125
- return mem
126
-
127
-
128
- def hosted_step(mem, p, tau, h, omega):
129
- """One step of the hosted transducer; returns (mem, p, h, omega)."""
130
- mem = list(mem)
131
- a, b, c = mem[p], mem[(p + 1) % 128], mem[(p + 2) % 128]
132
- a, b = a % 128, b % 128
133
- r = (mem[b] - mem[a]) % 256
134
- mem[b] = r
135
- p2 = c % 128 if (r == 0 or r >= 128) else (p + 3) % 128
136
- cwr, crd, crw = mem[mv(C_WR)] == 1, mem[mv(C_RD)] == 1, mem[mv(C_RW)] == 1
137
- omega = list(omega)
138
- if cwr:
139
- omega.append(mem[mv(R_OUT)])
140
- mem[mv(R_OUT)] = 0
141
- if crd:
142
- if h < len(tau):
143
- mem[mv(R_IN)] = tau[h]
144
- mem[mv(C_EOT)] = 0
145
- h += 1
146
- else:
147
- mem[mv(C_EOT)] = 1
148
- if crw:
149
- h = 0
150
- mem[mv(C_WR)] = mem[mv(C_RD)] = mem[mv(C_RW)] = 0
151
- return mem, p2, h, omega
152
-
153
-
154
- def run_hosted(tau, max_steps=100000):
155
- mem, p, h, omega = p128_image(), 0, 0, []
156
- for k in range(1, max_steps + 1):
157
- mem, p, h, omega = hosted_step(mem, p, tau, h, omega)
158
- if p == 0x7F:
159
- return k, mem, h, omega
160
- raise RuntimeError('no halt')
161
-
162
-
163
- def nu(x):
164
- return (-x) % 256
165
-
166
-
167
- def delta(r):
168
- out, i = [], 0
169
- while True:
170
- t = r[i]
171
- if t == 0:
172
- return bytes(out)
173
- if 1 <= t <= 127:
174
- out += [nu(b) for b in r[i + 1:i + 1 + t]]
175
- i += 1 + t
176
- elif t >= 129:
177
- out += [nu(r[i + 1])] * (256 - t)
178
- i += 2
179
- else:
180
- raise ValueError('tag 128')
181
-
182
-
183
- def describe(f):
184
- """The encoder of Lemma 4.4."""
185
- out, i = [], 0
186
- while i < len(f):
187
- ell = 1
188
- while i + ell < len(f) and f[i + ell] == f[i] and ell < 127:
189
- ell += 1
190
- if ell >= 4:
191
- out += [256 - ell, nu(f[i])]
192
- i += ell
193
- continue
194
- k = i + 1
195
- while k < len(f) and k < i + 127:
196
- if k + 3 < len(f) and f[k] == f[k + 1] == f[k + 2] == f[k + 3]:
197
- break
198
- k += 1
199
- out += [k - i] + [nu(c) for c in f[i:k]]
200
- i = k
201
- return bytes(out + [0])
202
-
203
-
204
- def census_steps(r):
205
- """Theorem 4.6: 5 f_lit + 4 f_rep + 7L + 9R + 8."""
206
- i, steps = 0, 8
207
- while r[i] != 0:
208
- t = r[i]
209
- if t <= 127:
210
- steps += 5 * t + 7
211
- i += 1 + t
212
- else:
213
- steps += 4 * (256 - t) + 9
214
- i += 2
215
- return steps
216
-
217
-
218
- def random_recipe(rng, max_len=64, max_out=63):
219
- while True:
220
- toks, out_len = [], 0
221
- for _ in range(rng.randint(0, 6)):
222
- if rng.random() < 0.5:
223
- n = rng.randint(1, 12)
224
- toks += [n] + [rng.randrange(256) for _ in range(n)]
225
- out_len += n
226
- else:
227
- ell = rng.randint(1, 20)
228
- toks += [256 - ell, rng.randrange(256)]
229
- out_len += ell
230
- r = toks + [0]
231
- if len(r) <= max_len and out_len <= max_out:
232
- return bytes(r)
233
-
234
-
235
- # ---------------------------------------------------------------------------
236
- # Theorem 9.10
237
- # ---------------------------------------------------------------------------
238
-
239
-
240
- def hosted_check(recipes_n=40, random_n=2000, seed=11):
241
- import hosted_constructor as HC
242
- from reflect import Cfg, encode_netlist
243
- A, G, span, mem_base, tape_base, out_base, work_base = HC.layout(64, 64, min_records=520)
244
- cfg = Cfg(A=A, G=G, banks=1, self_mod=False)
245
- m = HC.build_microprogram(cfg, mem_base, tape_base, out_base, 64, work_base, 700)
246
- bits = encode_netlist(cfg, m.recs)
247
- M = Member(A, G, span)
248
- assert (A, G, span, mem_base, tape_base, out_base) == (15, 535, 2808, 721, 1745, 2257)
249
- named = m.named
250
- PC, HEAD, WP = named['pc'] if 'pc' in named else named['PC'], named['head'], named['wp']
251
- n_real = len(m.recs)
252
-
253
- def blank():
254
- x = [0] * M.S
255
- x[M.Q0:M.Q0 + len(bits)] = bits
256
- return x
257
-
258
- def put_reg(x, base, v):
259
- for k in range(7):
260
- x[base + k] = (v >> k) & 1
261
-
262
- def get_reg(x, base):
263
- return sum(x[base + k] << k for k in range(7))
264
-
265
- def put_bytes(x, base, stride, data):
266
- for j, byte in enumerate(data):
267
- for t in range(8):
268
- x[base + t * stride + j] = (byte >> t) & 1
269
-
270
- def get_bytes(x, base, stride, n):
271
- return [sum(x[base + t * stride + j] << t for t in range(8)) for j in range(n)]
272
-
273
- res = dict(records=n_real, noops=G - n_real)
274
- # every record after the 494th is a no-op writing the sink
275
- tail = [decode_record(bits[k * M.b:(k + 1) * M.b], A) for k in range(n_real, G)]
276
- res['tail_noops'] = all(r[1] == 0 and r[4] == 0 and r[6] == -1 and r[7] == A + 5 and r[8] == 0 for r in tail)
277
- # the decoded records equal the artifact's own description of them
278
- same = True
279
- for k, (slots, bias, (oa, oi)) in enumerate(m.recs):
280
- sl = list(slots) + [(0, 0, 0)] * (2 - len(slots))
281
- rec = decode_record(bits[k * M.b:(k + 1) * M.b], A)
282
- same &= rec == (sl[0][0], sl[0][1], sl[0][2], sl[1][0], sl[1][1], sl[1][2], bias, oa, oi)
283
- res['decode_matches'] = same
284
- # recipes run to the halt
285
- rng = random.Random(seed)
286
- tests = [b'ABCCCCCCCCDE']
287
- runs = []
288
- for k in range(recipes_n + 1):
289
- if k == 0:
290
- target = tests[0]
291
- r = describe(target)
292
- assert delta(r) == target and r == bytes([2, nu(65), nu(66), 248, nu(67), 2, nu(68), nu(69), 0])
293
- else:
294
- r = random_recipe(rng)
295
- tau = list(r) + [0] * (64 - len(r))
296
- steps, emem, eh, eomega = run_hosted(tau)
297
- assert steps == census_steps(r) and bytes(eomega) == delta(r)
298
- x = blank()
299
- put_bytes(x, mem_base, 128, p128_image())
300
- put_bytes(x, tape_base, 64, tau)
301
- I = Interp(M, x)
302
- apps = 0
303
- while not I.halt and apps < (steps + 2) * G:
304
- I.apply()
305
- apps += 1
306
- passes = -(-apps // G)
307
- got_out = bytes(get_bytes(x, out_base, 64, 64))
308
- ok = (I.halt and passes == steps and got_out[:len(eomega)] == bytes(eomega)
309
- and not any(got_out[len(eomega):]) and get_bytes(x, mem_base, 128, 128) == emem
310
- and get_reg(x, HEAD) == eh == len(r) and get_reg(x, WP) == len(eomega) and not I.out)
311
- runs.append(dict(recipe_len=len(r), out_len=len(eomega), steps=steps, passes=passes,
312
- applications=apps, ok=ok))
313
- res['recipes'] = len(runs)
314
- res['recipes_ok'] = sum(r['ok'] for r in runs)
315
- res['first'] = runs[0]
316
- # one pass from random held states
317
- bad = 0
318
- halting = 0
319
- for _ in range(random_n):
320
- x = blank()
321
- for c in range(M.A + 6, mem_base): # the work region, temporaries included, random
322
- x[c] = rng.randrange(2)
323
- for c in range(M.A):
324
- x[c] = rng.randrange(2)
325
- x[M.OUT_DATA] = rng.randrange(2)
326
- x[M.ADV] = x[M.OUT_STROBE] = x[M.HALT] = 0
327
- mem = [rng.randrange(256) for _ in range(128)]
328
- p = rng.randrange(127)
329
- u = rng.random()
330
- if u < 0.125:
331
- p = 0x7C # p + 3 is the halt address
332
- elif u < 0.25:
333
- mem[(p + 2) % 128] = 0x7F # the branch target is the halt address
334
- tau = [rng.randrange(256) for _ in range(64)]
335
- h = rng.randint(0, 64)
336
- w = rng.randint(0, 63)
337
- omega = [rng.randrange(256) for _ in range(w)]
338
- put_bytes(x, mem_base, 128, mem)
339
- put_bytes(x, tape_base, 64, tau)
340
- put_bytes(x, out_base, 64, omega + [0] * (64 - w))
341
- put_reg(x, PC, p); put_reg(x, HEAD, h); put_reg(x, WP, w)
342
- emem, ep, eh, eomega = hosted_step(mem, p, tau, h, omega)
343
- I = Interp(M, x)
344
- for _ in range(G):
345
- I.apply()
346
- got_out = get_bytes(x, out_base, 64, 64)
347
- common = (get_bytes(x, mem_base, 128, 128) == emem and get_reg(x, HEAD) == eh
348
- and get_reg(x, WP) == len(eomega) and got_out[:len(eomega)] == eomega
349
- and not any(got_out[len(eomega):]) and get_bytes(x, tape_base, 64, 64) == tau
350
- and not I.out)
351
- if ep == 0x7F:
352
- halting += 1
353
- ok = common and I.halt
354
- else:
355
- ok = common and not I.halt and get_reg(x, PC) == ep and I.g == 0
356
- bad += not ok
357
- res['random_states'] = random_n
358
- res['random_halting_steps'] = halting
359
- res['random_disagree'] = bad
360
- return res
361
-
362
-
363
- # ---------------------------------------------------------------------------
364
- # Proposition 9.8
365
- # ---------------------------------------------------------------------------
366
-
367
-
368
- def hierarchy_check(states=48, passes=4, seed=5):
369
- from reflect import Cfg, build_net, encode_netlist
370
- small = Cfg(A=7, G=3, banks=1, self_mod=False)
371
- net, inputs, outputs = build_net(small)
372
- # the netlist as units over named signals; inputs are the state coordinates in order
373
- gates = net.gates
374
- order, seen = [], set(inputs) | {'#0', '#1'}
375
- pending = dict(gates)
376
- while pending: # a topological order of the units
377
- prog = [g for g, (ins, _) in pending.items() if all(s in seen for s, _ in ins)]
378
- assert prog
379
- for g in prog:
380
- order.append(g); seen.add(g); del pending[g]
381
- # fan-in two (Corollary 9.6): every unit of fan-in > 2 must be a conjunction or a disjunction
382
- units2 = [] # (name, [(sig, w)], bias)
383
- cnt = [0]
384
-
385
- def fresh():
386
- cnt[0] += 1
387
- return f'_t{cnt[0]}'
388
-
389
- def kind(ins, b):
390
- pos = sum(1 for _, w in ins if w > 0)
391
- neg = len(ins) - pos
392
- if b == -pos:
393
- return 'and'
394
- if b == neg - 1:
395
- return 'or'
396
- return None
397
-
398
- def tree(name, k, lits):
399
- """lits: [(sig, polarity)], a balanced tree of two-input units, the root named name."""
400
- level = list(lits)
401
- while len(level) > 2:
402
- nxt = []
403
- for i in range(0, len(level) - 1, 2):
404
- t = fresh()
405
- (s1, p1), (s2, p2) = level[i], level[i + 1]
406
- pos = p1 + p2
407
- b = -pos if k == 'and' else (2 - pos) - 1
408
- units2.append((t, [(s1, 1 if p1 else -1), (s2, 1 if p2 else -1)], b))
409
- nxt.append((t, 1))
410
- if len(level) % 2:
411
- nxt.append(level[-1])
412
- level = nxt
413
- if len(level) == 2:
414
- (s1, p1), (s2, p2) = level
415
- pos = p1 + p2
416
- b = -pos if k == 'and' else (2 - pos) - 1
417
- units2.append((name, [(s1, 1 if p1 else -1), (s2, 1 if p2 else -1)], b))
418
- else:
419
- (s1, p1), = level
420
- units2.append((name, [(s1, 1 if p1 else -1)], -1 if p1 else 0))
421
- for g in order:
422
- ins, b = gates[g]
423
- if len(ins) <= 2:
424
- units2.append((g, list(ins), b))
425
- else:
426
- k = kind(ins, b)
427
- assert k is not None, (g, ins, b)
428
- tree(g, k, [(s, 1 if w > 0 else 0) for s, w in ins])
429
- # constants are wired as units
430
- consts = {}
431
- # the coordinates whose next value is not the coordinate itself
432
- changed = [i for i, o in enumerate(outputs) if o != inputs[i]]
433
- nstate = len(inputs)
434
- n2 = len(units2)
435
- # the larger member: A' = 17, one bank, G' = 2088, span 1616
436
- big = Member(17, 2088, 1616)
437
- base = big.A + 6
438
- addr = {s: base + i for i, s in enumerate(inputs)}
439
- for i, (name, _, _) in enumerate(units2):
440
- addr[name] = base + nstate + i
441
- recs = []
442
- for name, ins, b in units2:
443
- sl = [(addr[s] if s not in ('#0', '#1') else None, w) for s, w in ins]
444
- assert all(a is not None for a, _ in sl)
445
- sl += [(0, 0)] * (2 - len(sl))
446
- recs.append((sl[0][0], sl[0][1], 0, sl[1][0], sl[1][1], 0, b, addr[name], 0))
447
- for i in changed:
448
- src = outputs[i]
449
- if src == '#0':
450
- recs.append((0, 0, 0, 0, 0, 0, -1, addr[inputs[i]], 0))
451
- elif src == '#1':
452
- recs.append((0, 0, 0, 0, 0, 0, 0, addr[inputs[i]], 0))
453
- else:
454
- recs.append((addr[src], 1, 0, 0, 0, 0, -1, addr[inputs[i]], 0))
455
- assert len(recs) <= big.G and base + nstate + n2 <= big.span
456
- noop = (0, 0, 0, 0, 0, 0, -1, big.A + 5, 0)
457
- recs += [noop] * (big.G - len(recs))
458
- bits = sum((encode_record(r, big.A) for r in recs), [])
459
- rng = random.Random(seed)
460
-
461
- def eval_small(vals):
462
- v = dict(zip(inputs, vals)); v['#0'], v['#1'] = 0, 1
463
- for g in order:
464
- ins, b = gates[g]
465
- v[g] = 1 if b + sum(w * v[s] for s, w in ins) >= 0 else 0
466
- return [v[o] for o in outputs]
467
- bad = 0
468
- for _ in range(states):
469
- y = [rng.randrange(2) for _ in range(nstate)]
470
- x = [0] * big.S
471
- x[big.Q0:big.Q0 + len(bits)] = bits
472
- for i, s in enumerate(inputs):
473
- x[addr[s]] = y[i]
474
- I = Interp(big, x)
475
- for _ in range(passes):
476
- for _ in range(big.G):
477
- I.apply()
478
- y = eval_small(y)
479
- got = [x[addr[s]] for s in inputs]
480
- bad += got != y
481
- return dict(small_units=len(gates), small_state_bits=nstate, fanin2_units=n2,
482
- signals=nstate + n2, changed=len(changed), records=len(recs) - recs.count(noop),
483
- big=dict(A=big.A, G=big.G, b=big.b, span=big.span), states=states, passes=passes,
484
- disagree=bad)
485
-
486
-
487
- if __name__ == '__main__':
488
- out = {}
489
- for which, fn in (('hierarchy', hierarchy_check), ('hosted', hosted_check)):
490
- t0 = time.time()
491
- res = fn()
492
- res['seconds'] = round(time.time() - t0, 1)
493
- print(which, json.dumps(res), flush=True)
494
- out[which] = res
495
- json.dump(out, open(os.path.join(RUNS, 'independent_interpreter.json'), 'w'), indent=1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/paper_stats.py CHANGED
@@ -1,4 +1,4 @@
1
- """Structural statistics for the host, its levelization, and the interpreter.
2
 
3
  Reproduces the level assignment of matrix8.compile_net without materialising the
4
  dense matrices, so the layer widths, the number of identity units, the total
@@ -208,33 +208,6 @@ def main() -> int:
208
  print(f" widths {lev['widths']}")
209
  out["lev_host"] = lev
210
 
211
- print()
212
- print("=" * 74)
213
- print(" Interpreter U (reflect.Cfg default)")
214
- print("=" * 74)
215
- from reflect import Cfg, build_net
216
- cfg = Cfg()
217
- unet, uin, uout = build_net(cfg)
218
- ulev = levelize(unet, uin, uout)
219
- for k in ("depth", "gates", "netlist_gates", "state_bits", "M",
220
- "dense_entries", "identity_units",
221
- "new_units", "max_fanin_netlist", "nonzero_weights_netlist",
222
- "bias_min", "bias_max"):
223
- print(f" {k:<26} {ulev[k]:,}" if isinstance(ulev[k], int) else f" {k:<26} {ulev[k]}")
224
- print(f" fan-in histogram {ulev['fanin_hist']}")
225
- print(f" cfg: S={cfg.S} G={cfg.G} F={cfg.F} BB={cfg.BB} banks={cfg.banks} "
226
- f"record_bits={cfg.R} acc_bits={cfg.ACC} slot={cfg.SLOT}")
227
- print(f" regions: PTR@{cfg.PTR_BASE} width {cfg.A}; control {cfg.OUT_DATA}..{cfg.WORK_BASE-1}; "
228
- f"data {cfg.WORK_BASE}..{cfg.NET0-1} ({cfg.NET0-cfg.WORK_BASE} bits); "
229
- f"Q0 {cfg.NET0}..{cfg.NET1-1}; Q1 {cfg.NET1}..{cfg.S-1}; "
230
- f"|Q|={cfg.G*cfg.R} b={cfg.R}")
231
- out["lev_U"] = ulev
232
- out["U_cfg"] = {"S": cfg.S, "G": cfg.G, "F": cfg.F, "BB": cfg.BB,
233
- "banks": cfg.banks, "R": cfg.R, "ACC": cfg.ACC,
234
- "SLOT": cfg.SLOT, "A": cfg.A, "NET0": cfg.NET0,
235
- "NET1": cfg.NET1, "WORK_BASE": cfg.WORK_BASE,
236
- "span": cfg.span, "STATE_BITS": cfg.STATE_BITS}
237
-
238
  path = runs_path("paper_stats.json")
239
  json.dump(out, open(path, "w"), indent=1)
240
  print(f"\nwrote {path}")
 
1
+ """Structural statistics for the host and its levelization.
2
 
3
  Reproduces the level assignment of matrix8.compile_net without materialising the
4
  dense matrices, so the layer widths, the number of identity units, the total
 
208
  print(f" widths {lev['widths']}")
209
  out["lev_host"] = lev
210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  path = runs_path("paper_stats.json")
212
  json.dump(out, open(path, "w"), indent=1)
213
  print(f"\nwrote {path}")
src/paper_verify.py CHANGED
@@ -1,7 +1,5 @@
1
- """Fast verification for the paper: codec, framing, serialization, interpreter.
2
-
3
- Everything here finishes in minutes. The long generation runs are in
4
- paper_runs.py.
5
  """
6
 
7
  from __future__ import annotations
@@ -229,63 +227,10 @@ def check_host_datapath():
229
  "counter_pairs": 65536, "counter_failures": bad_ctr}
230
 
231
 
232
- # ---------------------------------------------------------------------------
233
- def check_interpreter():
234
- """One-record semantics exhaustively over record fields, and the record counter."""
235
- print("[5] Interpreter U")
236
- import torch
237
- from reflect import (Cfg, build_net, Leveled, encode_netlist, state_to_vec,
238
- vec_to_state)
239
- cfg = Cfg()
240
- net, inputs, outputs = build_net(cfg)
241
- lev = Leveled(net, inputs, outputs, device="cpu")
242
-
243
- def U(sig, gp, halt=0):
244
- v = state_to_vec(cfg, {"sig": sig, "gp": gp, "halt": halt}).unsqueeze(0)
245
- out = lev.step(v[:, :len(inputs)])[0]
246
- return vec_to_state(cfg, out)
247
-
248
- ra, rb, ro = cfg.WORK_BASE, cfg.WORK_BASE + 1, cfg.WORK_BASE + 2
249
- bad = total = 0
250
- for w0 in (-1, 0, 1):
251
- for w1 in (-1, 0, 1):
252
- for bias in range(-(1 << (cfg.BB - 1)), 1 << (cfg.BB - 1)):
253
- nl = encode_netlist(cfg, [([(ra, w0, 0), (rb, w1, 0)], bias, (ro, 0))])
254
- for va in (0, 1):
255
- for vb in (0, 1):
256
- sig = [0] * cfg.S
257
- sig[cfg.NET0:cfg.NET0 + len(nl)] = nl
258
- sig[ra], sig[rb] = va, vb
259
- g = U(sig, 0)
260
- exp = 1 if w0 * va + w1 * vb + bias >= 0 else 0
261
- total += 1
262
- if g["sig"][ro] != exp or g["gp"] != 1:
263
- bad += 1
264
- print(f" one-record semantics over all {total} (w0,w1,bias,x0,x1) "
265
- f"configurations: {'exact' if bad == 0 else f'{bad} FAILED'}")
266
-
267
- # record counter advances modulo G
268
- cbad = 0
269
- nl = encode_netlist(cfg, [])
270
- for gp in range(cfg.G):
271
- sig = [0] * cfg.S
272
- sig[cfg.NET0:cfg.NET0 + len(nl)] = nl
273
- g = U(sig, gp)
274
- if g["gp"] != (gp + 1) % cfg.G:
275
- cbad += 1
276
- print(f" record counter advances mod G on all {cfg.G} values: "
277
- f"{'exact' if cbad == 0 else 'FAILED'}")
278
-
279
- R["interpreter"] = {
280
- "onerecord_cases": total, "onerecord_failures": bad,
281
- "counter_failures": cbad, "S": cfg.S, "G": cfg.G, "b": cfg.R,
282
- }
283
-
284
-
285
  # ---------------------------------------------------------------------------
286
  def check_lev_sparse_dense():
287
  """The sparse and dense evaluations of Lev(N_host) agree."""
288
- print("[6] Lev(N_host): sparse vs dense")
289
  import torch
290
  from selfrep import LevEvaluator, NetEvaluator, read_host
291
  sigma = read_host()
@@ -313,7 +258,6 @@ def main() -> int:
313
  check_framing()
314
  check_serialization()
315
  check_host_datapath()
316
- check_interpreter()
317
  check_lev_sparse_dense()
318
  R["seconds"] = time.perf_counter() - t0
319
  path = runs_path("paper_verify.json")
 
1
+ """The recipe codec, the framing, the serialization, the host's datapath and the
2
+ levelization, each against its definition; the record is paper/runs/paper_verify.json.
 
 
3
  """
4
 
5
  from __future__ import annotations
 
227
  "counter_pairs": 65536, "counter_failures": bad_ctr}
228
 
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  # ---------------------------------------------------------------------------
231
  def check_lev_sparse_dense():
232
  """The sparse and dense evaluations of Lev(N_host) agree."""
233
+ print("[5] Lev(N_host): sparse vs dense")
234
  import torch
235
  from selfrep import LevEvaluator, NetEvaluator, read_host
236
  sigma = read_host()
 
258
  check_framing()
259
  check_serialization()
260
  check_host_datapath()
 
261
  check_lev_sparse_dense()
262
  R["seconds"] = time.perf_counter() - t0
263
  path = runs_path("paper_verify.json")
src/stored_host.py ADDED
@@ -0,0 +1,694 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """The host as a stored network of the two-head machine.
2
+
3
+ The two-head machine T_{G,L} of Section 7 is taken with a window of W = 2^ALPHA cells,
4
+ so that a record (w0 code, w1 code, beta, a0, a1, ao) has 8 + 3 ALPHA bits. The host
5
+ H_8, read from its serialization sigma(N_host), is compiled into records of two
6
+ literals each, evaluated in an order in which every unit follows its inputs and the
7
+ next value of each state bit is written over that bit after the last record that
8
+ reads the old one; temporaries share window cells by live interval. One pass of the
9
+ records is one step of H_8 followed by the device of Definition 2.8 acting on the
10
+ tape under the read head, the idle bank under the arm and the copy tape under the
11
+ copy head. A startup block, active in the first pass after a hand-over, loads the
12
+ memory image m* of P*; the hand-over flips the bank bit when the host halts.
13
+
14
+ Run from the image of these records in the active bank and tau* = describe(image) on
15
+ the read tape, P* writes the image into the idle bank, copies tau* onto the copy
16
+ tape and halts, and the hand-over starts the copy.
17
+
18
+ python src/stored_host.py check one pass against H_8 and the device
19
+ python src/stored_host.py build the image, tau*, sizes and digests
20
+ python src/stored_host.py run [GENERATIONS] generations on the machine, in C
21
+ """
22
+ import hashlib
23
+ import json
24
+ import os
25
+ import random
26
+ import struct
27
+ import subprocess
28
+ import sys
29
+ import time
30
+
31
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
32
+ import selfrep as SR
33
+
34
+ REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
35
+ RECORD = os.path.join(REPO, 'paper', 'runs', 'paper_stored_host.json')
36
+
37
+ ALPHA = 12
38
+ W = 1 << ALPHA
39
+ B = 8 + 3 * ALPHA
40
+ KAPPA, RD, ARM, CPY, AR, AA, AC, SINK = range(8)
41
+ CW = 20 # width of the device's byte counters
42
+
43
+
44
+ # ---------------------------------------------------------------------------
45
+ # the host netlist from its serialization
46
+ # ---------------------------------------------------------------------------
47
+
48
+ def host_units():
49
+ data = SR.read_host()
50
+ hlen = struct.unpack('<Q', data[:8])[0]
51
+ head = json.loads(data[8:8 + hlen])
52
+ T = {}
53
+ import array
54
+ for k, spec in head.items():
55
+ if k == '__metadata__':
56
+ continue
57
+ lo, hi = spec['data_offsets']
58
+ a = array.array({'I8': 'b', 'I16': 'h', 'I32': 'i'}[spec['dtype']])
59
+ a.frombytes(data[8 + hlen + lo:8 + hlen + hi])
60
+ T[k] = list(a)
61
+ bias, fanin, pred, nxt = T['net.bias'], T['net.fanin'], T['net.pred'], T['net.next']
62
+ units, p = [], 0
63
+ for j in range(len(bias)):
64
+ units.append((bias[j], [(abs(e) - 1, 1 if e > 0 else -1) for e in pred[p:p + fanin[j]]]))
65
+ p += fanin[j]
66
+ return len(nxt), units, nxt
67
+
68
+
69
+ # state layout of sigma: pc bits 0..7 most significant first, halt 8, memory byte j bit k at 9 + 8 j + k,
70
+ # k = 0 the most significant
71
+ def mem_bit(j, t):
72
+ """State index of bit t (value 2^t) of memory byte j."""
73
+ return 9 + 8 * j + (7 - t)
74
+
75
+
76
+ PC_BIT = lambda t: 7 - t # bit t of the counter
77
+ HALT = 8
78
+
79
+
80
+ # ---------------------------------------------------------------------------
81
+ # records over named cells
82
+ # ---------------------------------------------------------------------------
83
+
84
+ class Prog:
85
+ def __init__(self):
86
+ self.recs = [] # (a0, w0, a1, w1, beta, ao), cells by name
87
+ self.uid = 0
88
+
89
+ def tmp(self):
90
+ self.uid += 1
91
+ return ('t', self.uid)
92
+
93
+ def emit(self, a0, w0, a1, w1, beta, ao):
94
+ assert -8 <= beta <= 7
95
+ self.recs.append((a0 if w0 else ('c', SINK), w0, a1 if w1 else ('c', SINK), w1, beta, ao))
96
+
97
+ def AND(self, x, y, dst):
98
+ (a, pa), (b, pb) = x, y
99
+ self.emit(a, 1 if pa else -1, b, 1 if pb else -1, -(pa + pb), dst)
100
+
101
+ def OR(self, x, y, dst):
102
+ (a, pa), (b, pb) = x, y
103
+ self.emit(a, 1 if pa else -1, b, 1 if pb else -1, 1 - pa - pb, dst)
104
+
105
+ def COPY(self, x, dst):
106
+ a, pa = x
107
+ self.emit(a, 1 if pa else -1, None, 0, -1 if pa else 0, dst)
108
+
109
+ def CONST(self, v, dst):
110
+ # a gate of one literal and its negation: OR is 1, AND is 0
111
+ x = ('c', SINK)
112
+ (self.OR if v else self.AND)((x, True), (x, False), dst)
113
+
114
+ def XOR(self, x, y, dst):
115
+ t1, t2 = self.tmp(), self.tmp()
116
+ self.OR(x, y, t1)
117
+ self.OR((x[0], not x[1]), (y[0], not y[1]), t2)
118
+ self.AND((t1, True), (t2, True), dst)
119
+
120
+ def MUX(self, s, a, b, dst):
121
+ """dst <- s ? a : b, s a cell, a and b literals."""
122
+ t1, t2 = self.tmp(), self.tmp()
123
+ self.AND((s, True), a, t1)
124
+ self.AND((s, False), b, t2)
125
+ self.OR((t1, True), (t2, True), dst)
126
+
127
+ def chain(self, kind, lits, dst, before=None):
128
+ """kind 'and' or 'or' of the literals into dst; before(i) emits literal i's signal
129
+ just ahead of its use. dst receives one write, the last."""
130
+ assert lits
131
+ if len(lits) == 1:
132
+ if before:
133
+ before(0)
134
+ self.COPY(lits[0], dst)
135
+ return
136
+ g = self.AND if kind == 'and' else self.OR
137
+ acc = self.tmp()
138
+ if before:
139
+ before(0); before(1)
140
+ if len(lits) == 2:
141
+ g(lits[0], lits[1], dst)
142
+ return
143
+ g(lits[0], lits[1], acc)
144
+ for i in range(2, len(lits)):
145
+ if before:
146
+ before(i)
147
+ g((acc, True), lits[i], dst if i == len(lits) - 1 else acc)
148
+
149
+ def threshold(self, t, lits, dst):
150
+ """at least t of the literals, by T_t(x1..xk) = T_t(x2..xk) or (x1 and T_{t-1}(x2..xk))."""
151
+ k = len(lits)
152
+ if t <= 0:
153
+ self.CONST(1, dst); return
154
+ if t > k:
155
+ self.CONST(0, dst); return
156
+ if t == 1:
157
+ self.chain('or', lits, dst); return
158
+ if t == k:
159
+ self.chain('and', lits, dst); return
160
+ a, b = self.tmp(), self.tmp()
161
+ self.threshold(t, lits[1:], a)
162
+ self.threshold(t - 1, lits[1:], b)
163
+ c = self.tmp()
164
+ self.AND(lits[0], (b, True), c)
165
+ self.OR((a, True), (c, True), dst)
166
+
167
+
168
+ def compile_host(P):
169
+ """The records of one step of H_8; state bits are cells ('s', i). A unit read by a single
170
+ unit is emitted just ahead of its use; the next value of each state bit is written over
171
+ the bit after every record that reads the old value."""
172
+ n, units, nxt = host_units()
173
+ cons = {}
174
+ for u, (_, ins) in enumerate(units):
175
+ for s, _w in ins:
176
+ if s >= n:
177
+ cons.setdefault(s - n, []).append(u)
178
+ is_next = {nxt[i] - n: i for i in range(n)}
179
+ assert len(is_next) == n and all(v >= n for v in nxt)
180
+ fusable = {u for u, c in cons.items() if len(c) == 1}
181
+ done, cell = set(), {}
182
+
183
+ def cell_of(s):
184
+ return ('s', s) if s < n else cell[s - n]
185
+
186
+ def ensure_ancestors(u):
187
+ for s, _w in units[u][1]:
188
+ if s >= n:
189
+ v = s - n
190
+ if v in fusable:
191
+ ensure_ancestors(v)
192
+ else:
193
+ need(v)
194
+
195
+ def need(u):
196
+ if u in done:
197
+ return
198
+ assert u not in is_next, 'a next value is read before the state is written'
199
+ ensure_ancestors(u)
200
+ cell[u] = P.tmp()
201
+ emit_unit(u, cell[u])
202
+ done.add(u)
203
+
204
+ def fused(v):
205
+ if v not in done:
206
+ cell[v] = P.tmp()
207
+ emit_unit(v, cell[v])
208
+ done.add(v)
209
+
210
+ def emit_unit(u, dst):
211
+ b, ins = units[u]
212
+ k = len(ins)
213
+ t = sum(1 for _, w in ins if w < 0) - b
214
+ lits = [None] * k
215
+
216
+ def before(i):
217
+ s = ins[i][0]
218
+ if s >= n and (s - n) in fusable:
219
+ fused(s - n)
220
+ lits[i] = (cell_of(s), ins[i][1] > 0)
221
+ if t <= 0 or t > k:
222
+ P.CONST(1 if t <= 0 else 0, dst)
223
+ elif 1 < t < k:
224
+ for i in range(k):
225
+ before(i)
226
+ P.threshold(t, lits, dst)
227
+ else:
228
+ _chain_lazy(P, 'and' if t == k else 'or', lits, dst, before)
229
+
230
+ order_next = [nxt[PC_BIT(t)] - n for t in range(8)] + [nxt[HALT] - n] + \
231
+ [nxt[mem_bit(j, t)] - n for j in range(256) for t in range(8)]
232
+ for u in order_next: # stage A: everything the next values read
233
+ ensure_ancestors(u)
234
+ for u in order_next: # stage B: the next values, in place
235
+ emit_unit(u, ('s', is_next[u]))
236
+ done.add(u)
237
+ return n, units, nxt
238
+
239
+
240
+ def _chain_lazy(P, kind, lits, dst, before):
241
+ k = len(lits)
242
+ if k == 1:
243
+ before(0)
244
+ P.COPY(lits[0], dst)
245
+ return
246
+ g = P.AND if kind == 'and' else P.OR
247
+ before(0); before(1)
248
+ if k == 2:
249
+ g(lits[0], lits[1], dst)
250
+ return
251
+ acc = P.tmp()
252
+ g(lits[0], lits[1], acc)
253
+ for i in range(2, k):
254
+ before(i)
255
+ g((acc, True), lits[i], dst if i == k - 1 else acc)
256
+
257
+
258
+ # ---------------------------------------------------------------------------
259
+ # the device, the startup block and the hand-over
260
+ # ---------------------------------------------------------------------------
261
+
262
+ def mem_cell(j, t):
263
+ return ('s', mem_bit(j, t))
264
+
265
+
266
+ def byte_is_one(P, j):
267
+ """[memory byte j == 1]"""
268
+ dst = P.tmp()
269
+ lits = [(mem_cell(j, 0), True)] + [(mem_cell(j, t), False) for t in range(1, 8)]
270
+ P.chain('and', lits, dst)
271
+ return dst
272
+
273
+
274
+ def compile_device(P):
275
+ cnt = [('d', 'cnt', i) for i in range(CW)]
276
+ lim = [('d', 'lim', i) for i in range(CW)]
277
+ dest = ('d', 'dest')
278
+ wr, rd, rw = byte_is_one(P, SR.C_WR), byte_is_one(P, SR.C_RD), byte_is_one(P, SR.C_RW)
279
+ # [cnt == lim]
280
+ eq = P.tmp()
281
+ xs = []
282
+ for i in range(CW):
283
+ e = P.tmp()
284
+ a1, a0 = P.tmp(), P.tmp()
285
+ P.AND((cnt[i], True), (lim[i], True), a1)
286
+ P.AND((cnt[i], False), (lim[i], False), a0)
287
+ P.OR((a1, True), (a0, True), e)
288
+ xs.append((e, True))
289
+ P.chain('and', xs, eq)
290
+ eot = P.tmp() # the end of the tape is known after the rewind
291
+ P.AND((eq, True), (dest, True), eot)
292
+ # (i) R_out, most significant bit first, under the arm or under the copy head
293
+ ca, cc = P.tmp(), P.tmp()
294
+ P.AND((wr, True), (dest, False), ca)
295
+ P.AND((wr, True), (dest, True), cc)
296
+ for t in range(7, -1, -1):
297
+ P.MUX(ca, (mem_cell(SR.R_OUT, t), True), (('c', ARM), True), ('c', ARM))
298
+ P.COPY((ca, True), ('c', AA))
299
+ P.MUX(cc, (mem_cell(SR.R_OUT, t), True), (('c', CPY), True), ('c', CPY))
300
+ P.COPY((cc, True), ('c', AC))
301
+ for t in range(8):
302
+ P.AND((mem_cell(SR.R_OUT, t), True), (wr, False), mem_cell(SR.R_OUT, t))
303
+ g = P.tmp()
304
+ P.AND((rd, True), (eot, False), g)
305
+ # (ii) the byte under the read head into R_in, most significant bit first, each bit turning the tape
306
+ for t in range(7, -1, -1):
307
+ P.MUX(g, (('c', RD), True), (mem_cell(SR.R_IN, t), True), mem_cell(SR.R_IN, t))
308
+ P.COPY((g, True), ('c', AR))
309
+ # c_eot: bit 0 <- [cnt == lim] on a read, the other bits cleared on a read
310
+ P.MUX(rd, (eot, True), (mem_cell(SR.C_EOT, 0), True), mem_cell(SR.C_EOT, 0))
311
+ for t in range(1, 8):
312
+ P.AND((mem_cell(SR.C_EOT, t), True), (rd, False), mem_cell(SR.C_EOT, t))
313
+ # the counter of bytes read: carries first, then the sum bits
314
+ car = [g]
315
+ for i in range(CW - 1):
316
+ c = P.tmp()
317
+ P.AND((car[-1], True), (cnt[i], True), c)
318
+ car.append(c)
319
+ for i in range(CW):
320
+ P.XOR((cnt[i], True), (car[i], True), cnt[i])
321
+ # (iii) a rewind: the limit takes the count, the count returns to 0, the writes go to the copy tape
322
+ for i in range(CW):
323
+ P.MUX(rw, (cnt[i], True), (lim[i], True), lim[i])
324
+ P.AND((cnt[i], True), (rw, False), cnt[i])
325
+ P.OR((dest, True), (rw, True), dest)
326
+ # (iv) the request cells cleared
327
+ for j in (SR.C_WR, SR.C_RD, SR.C_RW):
328
+ for t in range(8):
329
+ P.CONST(0, mem_cell(j, t))
330
+
331
+
332
+ def compile_startup(P, mem_image):
333
+ """Active while 'booted' is 0: the memory image, a zero counter and halt bit, the device
334
+ registers cleared; then 'booted' is set."""
335
+ b = ('d', 'booted')
336
+ for j in range(256):
337
+ for t in range(8):
338
+ c = mem_cell(j, t)
339
+ if (mem_image[j] >> t) & 1:
340
+ P.OR((c, True), (b, False), c) # b ? c : 1
341
+ else:
342
+ P.AND((c, True), (b, True), c) # b ? c : 0
343
+ for t in range(8):
344
+ P.AND((('s', PC_BIT(t)), True), (b, True), ('s', PC_BIT(t)))
345
+ P.AND((('s', HALT), True), (b, True), ('s', HALT))
346
+ for i in range(CW):
347
+ P.AND((('d', 'cnt', i), True), (b, True), ('d', 'cnt', i))
348
+ P.AND((('d', 'lim', i), True), (b, True), ('d', 'lim', i))
349
+ P.AND((('d', 'dest'), True), (b, True), ('d', 'dest'))
350
+ P.CONST(1, b)
351
+
352
+
353
+ def compile_handover(P):
354
+ """When the host has halted: the next pass starts up, and the bank bit flips at the last
355
+ record of the pass."""
356
+ h = ('s', HALT)
357
+ P.AND((('d', 'booted'), True), (h, False), ('d', 'booted'))
358
+ P.XOR((('c', KAPPA), True), (h, True), ('c', KAPPA))
359
+
360
+
361
+ # ---------------------------------------------------------------------------
362
+ # allocation and encoding
363
+ # ---------------------------------------------------------------------------
364
+
365
+ def allocate(recs):
366
+ """Window addresses: the special cells, the state, the device registers, then the
367
+ temporaries by live interval. Every temporary is written before it is read."""
368
+ fixed = {}
369
+ nxt_addr = 8
370
+ names = []
371
+ for r in recs:
372
+ for a in (r[0], r[2], r[5]):
373
+ if a is not None and a[0] in ('s', 'd') and a not in fixed:
374
+ names.append(a)
375
+ fixed[a] = None
376
+ for a in sorted(names, key=lambda x: (x[0], x[1:] if x[0] == 'd' else x[1])) :
377
+ fixed[a] = nxt_addr
378
+ nxt_addr += 1
379
+ first_w, last_r = {}, {}
380
+ for i, (a0, w0, a1, w1, be, ao) in enumerate(recs):
381
+ for a, w in ((a0, w0), (a1, w1)):
382
+ if w and a[0] == 't':
383
+ assert a in first_w, ('read before written', a)
384
+ last_r[a] = i
385
+ if ao[0] == 't':
386
+ first_w.setdefault(ao, i)
387
+ base = nxt_addr
388
+ free_at = {} # cell -> index after which it is free
389
+ addr = dict(fixed)
390
+ for name in sorted(first_w, key=lambda x: first_w[x]):
391
+ start, end = first_w[name], last_r.get(name, first_w[name])
392
+ cand = [c for c, f in free_at.items() if f < start]
393
+ c = min(cand) if cand else base + len(free_at)
394
+ addr[name] = c
395
+ free_at[c] = end
396
+ top = base + len(free_at)
397
+ assert top <= W, ('window too small', top)
398
+
399
+ def res(a):
400
+ if a is None:
401
+ return SINK
402
+ if a[0] == 'c':
403
+ return a[1]
404
+ return addr[a]
405
+ out = [(res(a0), w0, res(a1), w1, be, res(ao)) for (a0, w0, a1, w1, be, ao) in recs]
406
+ return out, addr, top
407
+
408
+
409
+ def encode_record(r):
410
+ a0, w0, a1, w1, beta, ao = r
411
+ bits = []
412
+ put = lambda v, w: bits.extend((v >> (w - 1 - j)) & 1 for j in range(w))
413
+ wc = {1: 1, -1: 2, 0: 0}
414
+ put(wc[w0], 2); put(wc[w1], 2); put(beta & 15, 4)
415
+ put(a0, ALPHA); put(a1, ALPHA); put(ao, ALPHA)
416
+ return bits
417
+
418
+
419
+ def decode_record(bits):
420
+ it = iter(bits)
421
+ get = lambda w: sum(next(it) << (w - 1 - j) for j in range(w))
422
+ c0 = get(2); c1 = get(2); beta = get(4)
423
+ a0 = get(ALPHA); a1 = get(ALPHA); ao = get(ALPHA)
424
+ wd = {1: 1, 2: -1, 0: 0, 3: 0}
425
+ return a0, wd[c0], a1, wd[c1], beta - 16 if beta >= 8 else beta, ao
426
+
427
+
428
+ def bits_to_bytes(bits):
429
+ assert len(bits) % 8 == 0
430
+ return bytes(sum(bits[i + k] << (7 - k) for k in range(8)) for i in range(0, len(bits), 8))
431
+
432
+
433
+ def bytes_to_bits(data):
434
+ return [(b >> (7 - k)) & 1 for b in data for k in range(8)]
435
+
436
+
437
+ def check_schedule(recs, lo, hi, n):
438
+ """Within the host block recs[lo:hi], every state bit is written once, by the record of its
439
+ next value, and read by no later record; in all records, no temporary is written after
440
+ its last read, so that temporaries with disjoint live intervals may share a cell."""
441
+ written = {}
442
+ for i in range(lo, hi):
443
+ ao = recs[i][5]
444
+ if ao[0] == 's':
445
+ assert ao not in written, ('state bit written twice', ao)
446
+ written[ao] = i
447
+ assert len(written) == n, 'a state bit without its next value'
448
+ for i in range(lo, hi):
449
+ a0, w0, a1, w1, _, _ = recs[i]
450
+ for a, w in ((a0, w0), (a1, w1)):
451
+ if w and a[0] == 's':
452
+ assert i <= written[a], ('state bit read after it is written', a)
453
+ last_r, last_w = {}, {}
454
+ for i, (a0, w0, a1, w1, _, ao) in enumerate(recs):
455
+ for a, w in ((a0, w0), (a1, w1)):
456
+ if w and a[0] == 't':
457
+ last_r[a] = i
458
+ if ao[0] == 't':
459
+ last_w[ao] = i
460
+ assert all(last_w[t] < last_r.get(t, last_w[t] + 1) or t not in last_r for t in last_w), \
461
+ 'a temporary written after its last read'
462
+ return dict(state_bits_written=len(written))
463
+
464
+
465
+ def build():
466
+ P = Prog()
467
+ compile_startup(P, SR.M_STAR)
468
+ lo = len(P.recs)
469
+ n, units, nxt = compile_host(P)
470
+ n_host = len(P.recs) - lo
471
+ check_schedule(P.recs, lo, len(P.recs), n)
472
+ compile_device(P)
473
+ compile_handover(P)
474
+ recs, addr, top = allocate(P.recs)
475
+ if len(recs) % 2: # an image of whole bytes
476
+ recs.insert(len(recs) - 3, (SINK, 0, SINK, 0, -1, SINK))
477
+ image_bits = [b for r in recs for b in encode_record(r)]
478
+ image = bits_to_bytes(image_bits)
479
+ tau = SR.describe(image)
480
+ assert SR.decode(tau) == image
481
+ return dict(recs=recs, addr=addr, top=top, image=image, image_bits=image_bits, tau=tau,
482
+ n_host_records=n_host, host=(n, units, nxt))
483
+
484
+
485
+ # ---------------------------------------------------------------------------
486
+ # the machine, transcribed
487
+ # ---------------------------------------------------------------------------
488
+
489
+ class Machine:
490
+ """T_{G,L} with a window of W cells; banks and tapes listed from their heads."""
491
+ def __init__(self, Q0, Q1, D0, D1, window):
492
+ self.Q = [list(Q0), list(Q1)]
493
+ self.D = [list(D0), list(D1)]
494
+ self.h = [[0, 0], [0, 0]] # heads of the banks and of the tapes
495
+ self.w = list(window)
496
+ self.N, self.L = len(Q0), len(D0)
497
+
498
+ def cell(self, a):
499
+ k = self.w[KAPPA]
500
+ if a == RD:
501
+ return self.D[k][self.h[1][k]]
502
+ if a == ARM:
503
+ return self.Q[1 - k][self.h[0][1 - k]]
504
+ if a == CPY:
505
+ return self.D[1 - k][self.h[1][1 - k]]
506
+ return self.w[a]
507
+
508
+ def step(self, rec=None):
509
+ k = self.w[KAPPA]
510
+ if rec is None:
511
+ hq = self.h[0][k]
512
+ rec = decode_record([self.Q[k][(hq + i) % self.N] for i in range(B)])
513
+ a0, w0, a1, w1, beta, ao = rec
514
+ v = 1 if w0 * self.cell(a0) + w1 * self.cell(a1) + beta >= 0 else 0
515
+ if ao == ARM:
516
+ self.Q[1 - k][self.h[0][1 - k]] = v
517
+ elif ao == CPY:
518
+ self.D[1 - k][self.h[1][1 - k]] = v
519
+ elif ao != RD:
520
+ self.w[ao] = v
521
+ self.h[0][k] = (self.h[0][k] + B) % self.N
522
+ if self.w[AA]:
523
+ self.h[0][1 - k] = (self.h[0][1 - k] + 1) % self.N
524
+ if self.w[AR]:
525
+ self.h[1][k] = (self.h[1][k] + 1) % self.L
526
+ if self.w[AC]:
527
+ self.h[1][1 - k] = (self.h[1][1 - k] + 1) % self.L
528
+ self.w[AA] = self.w[AR] = self.w[AC] = 0
529
+
530
+
531
+ # ---------------------------------------------------------------------------
532
+ # one pass against H_8 and the device
533
+ # ---------------------------------------------------------------------------
534
+
535
+ def host_step(n, units, nxt, x):
536
+ sig = list(x) + [0] * len(units)
537
+ for u, (b, ins) in enumerate(units):
538
+ sig[n + u] = 1 if b + sum(w * sig[s] for s, w in ins) >= 0 else 0
539
+ return [sig[v] for v in nxt]
540
+
541
+
542
+ def check(trials=40, seed=3, verbose=False):
543
+ S = build()
544
+ recs, addr = S['recs'], S['addr']
545
+ n, units, nxt = S['host']
546
+ G = len(recs)
547
+ rng = random.Random(seed)
548
+ bad = 0
549
+ image_bits = [b for r in recs for b in encode_record(r)]
550
+ x0 = [0] * n # the state x_0(m*)
551
+ for j in range(256):
552
+ for t in range(8):
553
+ x0[mem_bit(j, t)] = (SR.M_STAR[j] >> t) & 1
554
+ for trial in range(trials):
555
+ # a random host state with random device registers, temporaries and tapes; every fifth
556
+ # state not yet booted, and in every third the requests hold random bytes
557
+ x = [rng.randrange(2) for _ in range(n)]
558
+ x[HALT] = 0 if trial % 4 else 1
559
+ booted = 0 if trial % 5 == 0 else 1
560
+ for j in (SR.C_WR, SR.C_RD, SR.C_RW):
561
+ v = rng.randrange(256) if trial % 3 == 0 else rng.choice((0, 1))
562
+ for t in range(8):
563
+ x[mem_bit(j, t)] = (v >> t) & 1
564
+ L = 64
565
+ cnt, lim = rng.randrange(8), rng.randrange(8)
566
+ dest = rng.randrange(2)
567
+ win = [rng.randrange(2) for _ in range(W)]
568
+ win[KAPPA] = 0
569
+ for i in range(n):
570
+ win[addr[('s', i)]] = x[i]
571
+ for i in range(CW):
572
+ win[addr[('d', 'cnt', i)]] = (cnt >> i) & 1
573
+ win[addr[('d', 'lim', i)]] = (lim >> i) & 1
574
+ win[addr[('d', 'dest')]] = dest
575
+ win[addr[('d', 'booted')]] = booted
576
+ win[AR] = win[AA] = win[AC] = 0
577
+ D0 = [rng.randrange(2) for _ in range(L)]
578
+ Q1 = [rng.randrange(2) for _ in range(len(image_bits))]
579
+ D1 = [rng.randrange(2) for _ in range(L)]
580
+ M = Machine(image_bits, Q1, D0, D1, win)
581
+ for g in range(G):
582
+ M.step(recs[g])
583
+ # expected: the startup if not booted, one step of H_8, then the device on the ring tapes
584
+ if not booted:
585
+ x, cnt, lim, dest = list(x0), 0, 0, 0
586
+ y = host_step(n, units, nxt, x)
587
+ get = lambda j: sum(y[mem_bit(j, t)] << t for t in range(8))
588
+
589
+ def put(j, v):
590
+ for t in range(8):
591
+ y[mem_bit(j, t)] = (v >> t) & 1
592
+ e_q1, e_d1, hq1, hd0, hd1 = list(Q1), list(D1), 0, 0, 0
593
+ e_cnt, e_lim, e_dest = cnt, lim, dest
594
+ if get(SR.C_WR) == 1:
595
+ byte = get(SR.R_OUT)
596
+ for kk in range(8):
597
+ bit = (byte >> (7 - kk)) & 1
598
+ if e_dest:
599
+ e_d1[hd1] = bit; hd1 += 1
600
+ else:
601
+ e_q1[hq1] = bit; hq1 += 1
602
+ put(SR.R_OUT, 0)
603
+ if get(SR.C_RD) == 1:
604
+ if not (e_dest and e_cnt == e_lim):
605
+ put(SR.R_IN, sum(D0[hd0 + kk] << (7 - kk) for kk in range(8)))
606
+ hd0 += 8
607
+ put(SR.C_EOT, 0)
608
+ e_cnt = (e_cnt + 1) % (1 << CW)
609
+ else:
610
+ put(SR.C_EOT, 1)
611
+ if get(SR.C_RW) == 1:
612
+ e_lim, e_cnt, e_dest = e_cnt, 0, 1
613
+ put(SR.C_WR, 0); put(SR.C_RD, 0); put(SR.C_RW, 0)
614
+ e_kappa = 1 if y[HALT] else 0
615
+ got = [M.w[addr[('s', i)]] for i in range(n)]
616
+ g_cnt = sum(M.w[addr[('d', 'cnt', i)]] << i for i in range(CW))
617
+ g_lim = sum(M.w[addr[('d', 'lim', i)]] << i for i in range(CW))
618
+ parts = dict(state=got == y, cnt=g_cnt == e_cnt, lim=g_lim == e_lim,
619
+ dest=M.w[addr[('d', 'dest')]] == e_dest, bank=M.Q[1] == e_q1, copy=M.D[1] == e_d1,
620
+ arm=M.h[0][1] == hq1, read=M.h[1][0] == hd0, copyhead=M.h[1][1] == hd1,
621
+ kappa=M.w[KAPPA] == e_kappa, tape=M.D[0] == D0,
622
+ booted=M.w[addr[('d', 'booted')]] == 1 - y[HALT],
623
+ requests=M.w[AR] == M.w[AA] == M.w[AC] == 0, active=M.Q[0] == image_bits)
624
+ ok = all(parts.values())
625
+ if not ok and verbose:
626
+ print(trial, [k for k, v in parts.items() if not v], 'reqs',
627
+ [get(j) for j in (SR.C_WR, SR.C_RD, SR.C_RW)], 'dest', dest, 'cnt', cnt, 'lim', lim,
628
+ 'halt', x[HALT], [i for i in range(n) if got[i] != y[i]][:10])
629
+ bad += not ok
630
+ return dict(trials=trials, disagreements=bad, records=G, window_cells=S['top'])
631
+
632
+
633
+ def update_record(key, value):
634
+ rec = json.load(open(RECORD)) if os.path.exists(RECORD) else {}
635
+ rec[key] = value
636
+ json.dump(rec, open(RECORD, 'w'), indent=1)
637
+
638
+
639
+ def run(generations):
640
+ """Generations from the image in the active bank and tau* on the read tape, on the
641
+ machine in C, against the reference run of P* on tau*."""
642
+ S = build()
643
+ recs, image_bits, tau = S['recs'], S['image_bits'], S['tau']
644
+ G = len(recs)
645
+ out, steps = SR.run_reference(SR.M_STAR, tau)
646
+ assert out == S['image'] + tau
647
+ tau_bits = bytes_to_bits(tau)
648
+ work = os.path.join(REPO, 'variants')
649
+ inp, exe = os.path.join(work, '.stored_host.bin'), os.path.join(work, '.stored_host_run')
650
+ with open(inp, 'wb') as f:
651
+ f.write(struct.pack('<4i', W, ALPHA, G, len(tau_bits)))
652
+ f.write(bytes(image_bits) + bytes(tau_bits) + bytes(W))
653
+ subprocess.run(['gcc', '-O3', '-march=native', '-o', exe, os.path.join(REPO, 'src', 'stored_host_run.c')], check=True)
654
+ t0 = time.time()
655
+ p = subprocess.run([exe, inp, str(generations)], capture_output=True, text=True)
656
+ os.remove(inp); os.remove(exe)
657
+ lines = [json.loads(l) for l in p.stdout.split('\n') if l.strip()]
658
+ gens = [l for l in lines if 'generation' in l]
659
+ res = dict(records=G, window_cells=S['top'], record_bits=B, bank_bits=len(image_bits),
660
+ image_bytes=len(S['image']), tau_bytes=len(tau), passes_expected=steps,
661
+ generations=gens, seconds=round(time.time() - t0, 1),
662
+ sha256_image=hashlib.sha256(S['image']).hexdigest(),
663
+ sha256_tau=hashlib.sha256(tau).hexdigest())
664
+ for l in lines:
665
+ if 'state_after_3_equals_after_1' in l:
666
+ res['state_after_3_equals_after_1'] = l['state_after_3_equals_after_1']
667
+ res['every_generation_exact'] = all(g['passes'] == steps and g['steps'] == steps * G and g['bank_holds_image']
668
+ and g['tape_holds_tau'] for g in gens) and len(gens) == generations
669
+ update_record('run', res)
670
+ return res
671
+
672
+
673
+ if __name__ == '__main__':
674
+ cmd = sys.argv[1] if len(sys.argv) > 1 else 'build'
675
+ if cmd == 'check':
676
+ t0 = time.time()
677
+ res = check(int(sys.argv[2]) if len(sys.argv) > 2 else 40)
678
+ res['seconds'] = round(time.time() - t0, 1)
679
+ update_record('pass_check', res)
680
+ print(json.dumps(res))
681
+ sys.exit(0 if res['disagreements'] == 0 else 1)
682
+ elif cmd == 'run':
683
+ gens = int(sys.argv[2]) if len(sys.argv) > 2 else 3
684
+ res = run(gens)
685
+ print(json.dumps(res, indent=1))
686
+ sys.exit(0 if res['every_generation_exact'] and (gens < 3 or res.get('state_after_3_equals_after_1') == 1)
687
+ else 1)
688
+ elif cmd == 'build':
689
+ t0 = time.time()
690
+ S = build()
691
+ print(json.dumps(dict(records=len(S['recs']), host_records=S['n_host_records'],
692
+ window_cells=S['top'], record_bits=B, bank_bits=len(S['image_bits']),
693
+ image_bytes=len(S['image']), tau_bytes=len(S['tau']),
694
+ seconds=round(time.time() - t0, 1))))
src/stored_host_run.c ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Generations of the stored host on the two-head machine T_{G,L} with a window of W cells.
2
+ *
3
+ * stored_host_run IN.bin GENERATIONS
4
+ *
5
+ * IN.bin holds little-endian int32 W, ALPHA, G, L, then the G * (8 + 3 ALPHA) bits of the
6
+ * image, the L bits of tau*, and the W cells of the initial window, one byte per bit. The
7
+ * banks start with the image in Q0 and Q1 clear, the tapes with tau* on D0 and D1 clear.
8
+ * A step reads the record in front of the active bank, writes v = H(w0 x0 + w1 x1 + beta)
9
+ * into the window, under the arm or under the copy head, turns the active bank by one
10
+ * record and the other three by one cell on request, and clears the requests. The active
11
+ * bank is never written, so its records are decoded once each time the bank bit changes.
12
+ * After every generation, at the step that changes the bank bit, the program reports the
13
+ * steps it took, whether the new active bank holds the image and the new read tape holds
14
+ * tau*, the positions of the heads and a digest of the window.
15
+ */
16
+ #include <stdint.h>
17
+ #include <stdio.h>
18
+ #include <stdlib.h>
19
+ #include <string.h>
20
+
21
+ typedef struct { int a0, w0, a1, w1, beta, ao; } rec_t;
22
+
23
+ static int W, ALPHA, B, G, N, L;
24
+
25
+ static rec_t decode(const unsigned char *bits) {
26
+ int p = 0;
27
+ #define GET(w) ({ int v_ = 0; for (int j_ = 0; j_ < (w); j_++) v_ = (v_ << 1) | bits[p++]; v_; })
28
+ int c0 = GET(2), c1 = GET(2), beta = GET(4);
29
+ int a0 = GET(ALPHA), a1 = GET(ALPHA), ao = GET(ALPHA);
30
+ #undef GET
31
+ rec_t r;
32
+ r.w0 = c0 == 1 ? 1 : c0 == 2 ? -1 : 0;
33
+ r.w1 = c1 == 1 ? 1 : c1 == 2 ? -1 : 0;
34
+ r.beta = beta >= 8 ? beta - 16 : beta;
35
+ r.a0 = a0; r.a1 = a1; r.ao = ao;
36
+ return r;
37
+ }
38
+
39
+ static uint64_t fnv(const unsigned char *p, size_t n) {
40
+ uint64_t h = 1469598103934665603ULL;
41
+ for (size_t i = 0; i < n; i++) { h ^= p[i]; h *= 1099511628211ULL; }
42
+ return h;
43
+ }
44
+
45
+ int main(int argc, char **argv) {
46
+ FILE *f = fopen(argv[1], "rb");
47
+ int gens = atoi(argv[2]);
48
+ int hdr[4];
49
+ if (fread(hdr, 4, 4, f) != 4) return 1;
50
+ W = hdr[0]; ALPHA = hdr[1]; G = hdr[2]; L = hdr[3];
51
+ B = 8 + 3 * ALPHA; N = G * B;
52
+ unsigned char *image = malloc(N), *tau = malloc(L), *win = malloc(W);
53
+ if (fread(image, 1, N, f) != (size_t)N || fread(tau, 1, L, f) != (size_t)L || fread(win, 1, W, f) != (size_t)W) return 1;
54
+ fclose(f);
55
+ unsigned char *Q[2] = { malloc(N), calloc(N, 1) }, *D[2] = { malloc(L), calloc(L, 1) };
56
+ memcpy(Q[0], image, N); memcpy(D[0], tau, L);
57
+ long hq[2] = {0, 0}, hd[2] = {0, 0};
58
+ rec_t *recs = malloc(sizeof(rec_t) * G);
59
+ int k = win[0];
60
+ for (int g = 0; g < G; g++) recs[g] = decode(Q[k] + (size_t)g * B);
61
+ long ridx = 0; /* the record in front of the active bank */
62
+ if (gens == 0) { /* a fixed number of steps, for comparison */
63
+ long long n = atoll(argv[3]);
64
+ for (long long s = 0; s < n; s++) {
65
+ k = win[0];
66
+ const rec_t *r = &recs[ridx];
67
+ int ki = 1 - k;
68
+ int x0 = r->w0 ? (r->a0 == 1 ? D[k][hd[k]] : r->a0 == 2 ? Q[ki][hq[ki]] : r->a0 == 3 ? D[ki][hd[ki]] : win[r->a0]) : 0;
69
+ int x1 = r->w1 ? (r->a1 == 1 ? D[k][hd[k]] : r->a1 == 2 ? Q[ki][hq[ki]] : r->a1 == 3 ? D[ki][hd[ki]] : win[r->a1]) : 0;
70
+ int v = r->w0 * x0 + r->w1 * x1 + r->beta >= 0;
71
+ if (r->ao == 2) Q[ki][hq[ki]] = v; else if (r->ao == 3) D[ki][hd[ki]] = v; else if (r->ao != 1) win[r->ao] = v;
72
+ ridx = ridx + 1 == G ? 0 : ridx + 1;
73
+ hq[k] = (hq[k] + B) % N;
74
+ if (win[5]) hq[ki] = (hq[ki] + 1) % N;
75
+ if (win[4]) hd[k] = (hd[k] + 1) % L;
76
+ if (win[6]) hd[ki] = (hd[ki] + 1) % L;
77
+ win[4] = win[5] = win[6] = 0;
78
+ if (win[0] != k) { k = win[0]; for (int g = 0; g < G; g++) recs[g] = decode(Q[k] + (size_t)g * B); }
79
+ }
80
+ printf("{\"win\": \"%016llx\", \"q0\": \"%016llx\", \"q1\": \"%016llx\", \"d0\": \"%016llx\", \"d1\": \"%016llx\", \"heads\": [%ld, %ld, %ld, %ld]}\n",
81
+ (unsigned long long)fnv(win, W), (unsigned long long)fnv(Q[0], N), (unsigned long long)fnv(Q[1], N),
82
+ (unsigned long long)fnv(D[0], L), (unsigned long long)fnv(D[1], L), hq[0], hq[1], hd[0], hd[1]);
83
+ return 0;
84
+ }
85
+ unsigned char *first_state = NULL; size_t state_size = 0;
86
+ for (int gen = 1; gen <= gens; gen++) {
87
+ long long steps = 0;
88
+ for (;;) {
89
+ k = win[0];
90
+ const rec_t *r = &recs[ridx];
91
+ int ki = 1 - k;
92
+ #define CELL(a) ((a) == 1 ? D[k][hd[k]] : (a) == 2 ? Q[ki][hq[ki]] : (a) == 3 ? D[ki][hd[ki]] : win[(a)])
93
+ int x0 = r->w0 ? CELL(r->a0) : 0, x1 = r->w1 ? CELL(r->a1) : 0;
94
+ int v = r->w0 * x0 + r->w1 * x1 + r->beta >= 0;
95
+ int ao = r->ao;
96
+ if (ao == 2) Q[ki][hq[ki]] = v;
97
+ else if (ao == 3) D[ki][hd[ki]] = v;
98
+ else if (ao != 1) win[ao] = v;
99
+ ridx = ridx + 1 == G ? 0 : ridx + 1; /* the active bank turns by one record */
100
+ if (win[5]) hq[ki] = hq[ki] + 1 == N ? 0 : hq[ki] + 1;
101
+ if (win[4]) hd[k] = hd[k] + 1 == L ? 0 : hd[k] + 1;
102
+ if (win[6]) hd[ki] = hd[ki] + 1 == L ? 0 : hd[ki] + 1;
103
+ win[4] = win[5] = win[6] = 0;
104
+ steps++;
105
+ if (win[0] != k) break;
106
+ }
107
+ k = win[0];
108
+ /* the bank that was active turned by whole passes and is back at its first record */
109
+ if (ridx != 0) { fprintf(stderr, "hand-over inside a pass\n"); return 1; }
110
+ int bank_ok = memcmp(Q[k], image, N) == 0 && hq[k] == 0;
111
+ int tape_ok = memcmp(D[k], tau, L) == 0 && hd[k] == 0;
112
+ printf("{\"generation\": %d, \"steps\": %lld, \"passes\": %lld, \"kappa\": %d, \"bank_holds_image\": %d, "
113
+ "\"tape_holds_tau\": %d, \"heads\": [%ld, %ld, %ld, %ld], \"window_fnv\": \"%016llx\"}\n",
114
+ gen, steps, steps / G, k, bank_ok, tape_ok, hq[0], hq[1], hd[0], hd[1],
115
+ (unsigned long long)fnv(win, W));
116
+ fflush(stdout);
117
+ /* the full state after the first and the third generation */
118
+ size_t sz = (size_t)W + 2 * (size_t)N + 2 * (size_t)L;
119
+ unsigned char *st = malloc(sz);
120
+ memcpy(st, win, W); memcpy(st + W, Q[0], N); memcpy(st + W + N, Q[1], N);
121
+ memcpy(st + W + 2 * (size_t)N, D[0], L); memcpy(st + W + 2 * (size_t)N + L, D[1], L);
122
+ if (gen == 1) { first_state = st; state_size = sz; }
123
+ else {
124
+ if (gen == 3) printf("{\"state_after_3_equals_after_1\": %d}\n", memcmp(st, first_state, state_size) == 0);
125
+ free(st);
126
+ }
127
+ for (int g = 0; g < G; g++) recs[g] = decode(Q[k] + (size_t)g * B);
128
+ }
129
+ return 0;
130
+ }
src/twohead.py CHANGED
@@ -116,20 +116,20 @@ class Asm:
116
  return self
117
 
118
 
119
- def encode_record(r):
120
- """r = (a0, w0, a1, w1, beta, ao), laid out gate first."""
121
  a0, w0, a1, w1, beta, ao = r
122
  bits = []
123
  put = lambda val, w: bits.extend((val >> (w - 1 - j)) & 1 for j in range(w))
124
  wc = {1: 1, -1: 2, 0: 0}
125
- put(wc[w0], 2); put(wc[w1], 2); put(beta & 15, 4); put(a0, ALPHA); put(a1, ALPHA); put(ao, ALPHA)
126
  return bits
127
 
128
 
129
- def decode_record(bits):
130
  it = iter(bits)
131
  get = lambda w: sum(next(it) << (w - 1 - j) for j in range(w))
132
- c0 = get(2); c1 = get(2); beta = get(4); a0 = get(ALPHA); a1 = get(ALPHA); ao = get(ALPHA)
133
  wd = {1: 1, 2: -1, 0: 0, 3: 0}
134
  return a0, wd[c0], a1, wd[c1], beta - 16 if beta >= 8 else beta, ao
135
 
@@ -182,12 +182,14 @@ def translate(desc):
182
 
183
 
184
  class Machine:
185
- """The physics, transcribed: tapes listed from their heads."""
186
- def __init__(self, G, L, Q0, Q1, D0, D1, window):
187
- self.G, self.N, self.L = G, G * B, L
 
188
  self.Q = [list(Q0), list(Q1)]
189
  self.D = [list(D0), list(D1)]
190
  self.w = list(window)
 
191
  self.steps = 0
192
 
193
  def cell(self, a):
@@ -202,7 +204,7 @@ class Machine:
202
 
203
  def step(self):
204
  k = self.w[KAPPA]
205
- a0, w0, a1, w1, beta, ao = decode_record(self.Q[k][:B])
206
  val = 1 if w0 * self.cell(a0) + w1 * self.cell(a1) + beta >= 0 else 0
207
  if ao == ARM:
208
  self.Q[1 - k][0] = val
@@ -211,7 +213,7 @@ class Machine:
211
  elif ao != RD:
212
  self.w[ao] = val
213
  rot = lambda t, s: t[s:] + t[:s]
214
- self.Q[k] = rot(self.Q[k], B)
215
  if self.w[AA]:
216
  self.Q[1 - k] = rot(self.Q[1 - k], 1)
217
  if self.w[AR]:
 
116
  return self
117
 
118
 
119
+ def encode_record(r, alpha=ALPHA):
120
+ """r = (a0, w0, a1, w1, beta, ao), laid out gate first, with addresses of alpha bits."""
121
  a0, w0, a1, w1, beta, ao = r
122
  bits = []
123
  put = lambda val, w: bits.extend((val >> (w - 1 - j)) & 1 for j in range(w))
124
  wc = {1: 1, -1: 2, 0: 0}
125
+ put(wc[w0], 2); put(wc[w1], 2); put(beta & 15, 4); put(a0, alpha); put(a1, alpha); put(ao, alpha)
126
  return bits
127
 
128
 
129
+ def decode_record(bits, alpha=ALPHA):
130
  it = iter(bits)
131
  get = lambda w: sum(next(it) << (w - 1 - j) for j in range(w))
132
+ c0 = get(2); c1 = get(2); beta = get(4); a0 = get(alpha); a1 = get(alpha); ao = get(alpha)
133
  wd = {1: 1, 2: -1, 0: 0, 3: 0}
134
  return a0, wd[c0], a1, wd[c1], beta - 16 if beta >= 8 else beta, ao
135
 
 
182
 
183
 
184
  class Machine:
185
+ """The physics, transcribed: tapes listed from their heads; a window of 2^alpha cells."""
186
+ def __init__(self, G, L, Q0, Q1, D0, D1, window, alpha=ALPHA):
187
+ self.alpha, self.B = alpha, 8 + 3 * alpha
188
+ self.G, self.N, self.L = G, G * self.B, L
189
  self.Q = [list(Q0), list(Q1)]
190
  self.D = [list(D0), list(D1)]
191
  self.w = list(window)
192
+ assert len(self.w) == 1 << alpha
193
  self.steps = 0
194
 
195
  def cell(self, a):
 
204
 
205
  def step(self):
206
  k = self.w[KAPPA]
207
+ a0, w0, a1, w1, beta, ao = decode_record(self.Q[k][:self.B], self.alpha)
208
  val = 1 if w0 * self.cell(a0) + w1 * self.cell(a1) + beta >= 0 else 0
209
  if ao == ARM:
210
  self.Q[1 - k][0] = val
 
213
  elif ao != RD:
214
  self.w[ao] = val
215
  rot = lambda t, s: t[s:] + t[:s]
216
+ self.Q[k] = rot(self.Q[k], self.B)
217
  if self.w[AA]:
218
  self.Q[1 - k] = rot(self.Q[1 - k], 1)
219
  if self.w[AR]:
src/twohead_net.py CHANGED
@@ -1,14 +1,15 @@
1
  """The two-head physics as a netlist of ternary threshold units.
2
 
3
- The state bits are the 61 window cells other than 1, 2 and 3, then Q0, Q1, D0 and
4
- D1, each listed from its head. The netlist selects the record in front of the
5
- active bank by a multiplexer on kappa, decodes its three addresses, reads the two
6
- operands through the decode lines, forms v = H(w0 x0 + w1 x1 + beta) from the
7
- weight codes and the bias bits, writes v into the window, and computes every
8
- tape bit's next value: the active bank turned by 26 cells, the idle bank written
9
- under the arm and turned by one cell on request, and the two description tapes
10
- likewise. The unit order is the order of construction, which rocq/Phys.v follows
11
- unit for unit.
 
12
 
13
  python src/twohead_net.py check one step against the transcription on random states
14
  python src/twohead_net.py export [PATH] the netlist file (default variants/twohead_physics.bin)
@@ -59,7 +60,10 @@ class Net:
59
  return self.OR((self.AND((s, True), (a, True)), True), (self.AND((s, False), (b, True)), True))
60
 
61
 
62
- def build(G, L):
 
 
 
63
  N = G * B
64
  real = [0] + list(range(4, W)) # window cells held in the state
65
  wpos = {a: i for i, a in enumerate(real)}
@@ -70,7 +74,8 @@ def build(G, L):
70
  kap = wpos[KAPPA]
71
  # the record in front of the active bank
72
  f = [net.MUX(kap, q1 + j, q0 + j) for j in range(B)]
73
- c0, c1, be, a0, a1, ao = f[0:2], f[2:4], f[4:8], f[8:14], f[14:20], f[20:26]
 
74
  # the cells under the three heads
75
  rd = net.MUX(kap, d1, d0)
76
  armv = net.MUX(kap, q0, q1)
@@ -81,8 +86,8 @@ def build(G, L):
81
  def decode(field):
82
  lines = []
83
  for a in range(W):
84
- bits = [(a >> (ALPHA - 1 - j)) & 1 for j in range(ALPHA)]
85
- lines.append(net.u(-sum(bits), [(field[j], 1 if bits[j] else -1) for j in range(ALPHA)]))
86
  return lines
87
 
88
  l0, l1, lo = decode(a0), decode(a1), decode(ao)
@@ -142,7 +147,16 @@ def build(G, L):
142
  for i in range(L):
143
  nxt[base + i] = net.MUX(rot, hat((i + 1) % L), hat(i))
144
  assert all(x is not None for x in nxt)
145
- return net, nxt, dict(real=real, q0=q0, q1=q1, d0=d0, d1=d1, n=n, N=N, L=L)
 
 
 
 
 
 
 
 
 
146
 
147
 
148
  def stats(net, nxt):
@@ -165,19 +179,22 @@ def encode(M, lay):
165
  return [M.w[a] for a in lay['real']] + M.Q[0] + M.Q[1] + M.D[0] + M.D[1]
166
 
167
 
168
- def check(G, L, trials, seed=7):
169
- """One step of the netlist against the transcription from uniformly random full states."""
170
- net, nxt, lay = build(G, L)
 
171
  rnd = random.Random(seed)
172
  bad = 0
 
173
  for _ in range(trials):
174
- N = G * B
175
  bits = lambda k: [rnd.randint(0, 1) for _ in range(k)]
176
- M = T.Machine(G, L, bits(N), bits(N), bits(L), bits(L), bits(W))
177
  y = evaluate(net, nxt, encode(M, lay))
178
  M.step()
179
  bad += y != encode(M, lay)
180
- return bad
 
 
181
 
182
 
183
  def netfile_bytes(net, nxt):
@@ -207,11 +224,13 @@ def main():
207
  cmd = sys.argv[1] if len(sys.argv) > 1 else 'check'
208
  if cmd == 'check':
209
  t0 = time.time()
210
- small = check(5, 40, 300)
211
  A, rho, desc, net, nxt, lay = organism_netlist()
212
- full = check(len(A.recs), len(desc), 20)
 
213
  res = dict(small=dict(G=5, L=40, states=300, disagreements=small),
214
  full=dict(G=len(A.recs), L=len(desc), states=20, disagreements=full),
 
215
  netlist=stats(net, nxt), seconds=round(time.time() - t0, 1))
216
  update_record('random_states', res)
217
  print(json.dumps(res, indent=1))
@@ -249,15 +268,24 @@ def main():
249
  subprocess.run(['ocamlfind', 'ocamlopt', '-package', 'stdlib', 'physnet.mli', 'physnet.ml', 'dump.ml',
250
  '-o', 'dump'], cwd=ex, check=True)
251
  A, rho, desc, net, nxt, lay = organism_netlist()
252
- ours = netfile_bytes(net, nxt)
253
  out = os.path.join(ex, 'phys_rocq.bin')
254
- subprocess.run([os.path.join(ex, 'dump'), str(len(A.recs) * B), str(len(desc)), out], check=True)
255
- theirs = open(out, 'rb').read()
 
 
 
 
 
 
 
 
 
 
 
256
  for f in os.listdir(ex):
257
  if f not in ('dump.ml', 'physnet.ml', 'physnet.mli'):
258
  os.remove(os.path.join(ex, f))
259
- res = dict(N=len(A.recs) * B, L=len(desc), identical=ours == theirs,
260
- sha256_generator=hashlib.sha256(ours).hexdigest(), sha256_rocq=hashlib.sha256(theirs).hexdigest())
261
  update_record('extraction', res)
262
  print(json.dumps(res, indent=1))
263
 
 
1
  """The two-head physics as a netlist of ternary threshold units.
2
 
3
+ For a window of 2^alpha cells and records of 8 + 3 alpha bits, the state bits are
4
+ the window cells other than 1, 2 and 3, then Q0, Q1, D0 and D1, each listed from its
5
+ head. The netlist selects the record in front of the active bank by a multiplexer
6
+ on kappa, decodes its three addresses, reads the two operands through the decode
7
+ lines, forms v = H(w0 x0 + w1 x1 + beta) from the weight codes and the bias bits,
8
+ writes v into the window, and computes every tape bit's next value: the active bank
9
+ turned by one record, the idle bank written under the arm and turned by one cell on
10
+ request, and the two description tapes likewise. The unit order is the order of
11
+ construction, which rocq/Phys.v follows unit for unit. The organism runs on the
12
+ member with alpha = 6, a window of 64 cells.
13
 
14
  python src/twohead_net.py check one step against the transcription on random states
15
  python src/twohead_net.py export [PATH] the netlist file (default variants/twohead_physics.bin)
 
60
  return self.OR((self.AND((s, True), (a, True)), True), (self.AND((s, False), (b, True)), True))
61
 
62
 
63
+ def build(G, L, alpha=ALPHA):
64
+ """The netlist N_{G,L} of the machine with a window of 2^alpha cells and records of
65
+ 8 + 3 alpha bits."""
66
+ W, B = 1 << alpha, 8 + 3 * alpha
67
  N = G * B
68
  real = [0] + list(range(4, W)) # window cells held in the state
69
  wpos = {a: i for i, a in enumerate(real)}
 
74
  kap = wpos[KAPPA]
75
  # the record in front of the active bank
76
  f = [net.MUX(kap, q1 + j, q0 + j) for j in range(B)]
77
+ c0, c1, be = f[0:2], f[2:4], f[4:8]
78
+ a0, a1, ao = f[8:8 + alpha], f[8 + alpha:8 + 2 * alpha], f[8 + 2 * alpha:8 + 3 * alpha]
79
  # the cells under the three heads
80
  rd = net.MUX(kap, d1, d0)
81
  armv = net.MUX(kap, q0, q1)
 
86
  def decode(field):
87
  lines = []
88
  for a in range(W):
89
+ bits = [(a >> (alpha - 1 - j)) & 1 for j in range(alpha)]
90
+ lines.append(net.u(-sum(bits), [(field[j], 1 if bits[j] else -1) for j in range(alpha)]))
91
  return lines
92
 
93
  l0, l1, lo = decode(a0), decode(a1), decode(ao)
 
147
  for i in range(L):
148
  nxt[base + i] = net.MUX(rot, hat((i + 1) % L), hat(i))
149
  assert all(x is not None for x in nxt)
150
+ return net, nxt, dict(real=real, q0=q0, q1=q1, d0=d0, d1=d1, n=n, N=N, L=L, alpha=alpha)
151
+
152
+
153
+ def sizes(alpha, N, L):
154
+ """The units and edges of N_{G,L} for a window of 2^alpha cells and banks of N bits,
155
+ counted block by block."""
156
+ W = 1 << alpha
157
+ return dict(units=8 * W + 9 * alpha + 81 + 8 * N + 6 * L,
158
+ edges=3 * alpha * W + 12 * W + 18 * alpha + 170 + 18 * N + 12 * L,
159
+ state_bits=W - 3 + 2 * N + 2 * L)
160
 
161
 
162
  def stats(net, nxt):
 
179
  return [M.w[a] for a in lay['real']] + M.Q[0] + M.Q[1] + M.D[0] + M.D[1]
180
 
181
 
182
+ def check(G, L, trials, seed=7, alpha=ALPHA):
183
+ """One step of the netlist against the transcription from uniformly random full states;
184
+ returns the number of disagreements and the netlist's statistics."""
185
+ net, nxt, lay = build(G, L, alpha)
186
  rnd = random.Random(seed)
187
  bad = 0
188
+ N = G * (8 + 3 * alpha)
189
  for _ in range(trials):
 
190
  bits = lambda k: [rnd.randint(0, 1) for _ in range(k)]
191
+ M = T.Machine(G, L, bits(N), bits(N), bits(L), bits(L), bits(1 << alpha), alpha)
192
  y = evaluate(net, nxt, encode(M, lay))
193
  M.step()
194
  bad += y != encode(M, lay)
195
+ st = stats(net, nxt)
196
+ assert {k: st[k] for k in ('units', 'edges', 'state_bits')} == sizes(alpha, N, L)
197
+ return bad, st
198
 
199
 
200
  def netfile_bytes(net, nxt):
 
224
  cmd = sys.argv[1] if len(sys.argv) > 1 else 'check'
225
  if cmd == 'check':
226
  t0 = time.time()
227
+ small, _ = check(5, 40, 300)
228
  A, rho, desc, net, nxt, lay = organism_netlist()
229
+ full, _ = check(len(A.recs), len(desc), 20)
230
+ wide, st_wide = check(5, 40, 40, alpha=12)
231
  res = dict(small=dict(G=5, L=40, states=300, disagreements=small),
232
  full=dict(G=len(A.recs), L=len(desc), states=20, disagreements=full),
233
+ wide=dict(alpha=12, G=5, L=40, states=40, disagreements=wide, netlist=st_wide),
234
  netlist=stats(net, nxt), seconds=round(time.time() - t0, 1))
235
  update_record('random_states', res)
236
  print(json.dumps(res, indent=1))
 
268
  subprocess.run(['ocamlfind', 'ocamlopt', '-package', 'stdlib', 'physnet.mli', 'physnet.ml', 'dump.ml',
269
  '-o', 'dump'], cwd=ex, check=True)
270
  A, rho, desc, net, nxt, lay = organism_netlist()
 
271
  out = os.path.join(ex, 'phys_rocq.bin')
272
+ members = []
273
+ # the member that carries the organism, and a member with a window of 4096 cells
274
+ for alpha, G, L, built in ((ALPHA, len(A.recs), len(desc), (net, nxt)), (12, 5, 40, None)):
275
+ if built is None:
276
+ n2, x2, _ = build(G, L, alpha)
277
+ built = (n2, x2)
278
+ ours = netfile_bytes(*built)
279
+ N = G * (8 + 3 * alpha)
280
+ subprocess.run([os.path.join(ex, 'dump'), str(alpha), str(N), str(L), out], check=True)
281
+ theirs = open(out, 'rb').read()
282
+ members.append(dict(alpha=alpha, N=N, L=L, identical=ours == theirs,
283
+ sha256_generator=hashlib.sha256(ours).hexdigest(),
284
+ sha256_rocq=hashlib.sha256(theirs).hexdigest()))
285
  for f in os.listdir(ex):
286
  if f not in ('dump.ml', 'physnet.ml', 'physnet.mli'):
287
  os.remove(os.path.join(ex, f))
288
+ res = dict(members=members, identical=all(m['identical'] for m in members))
 
289
  update_record('extraction', res)
290
  print(json.dumps(res, indent=1))
291
 
src/verify.py DELETED
@@ -1,161 +0,0 @@
1
- """Every statement of the paper, checked, with the time each check takes.
2
-
3
- The checks are in three tiers, and the checks of a tier run concurrently. The
4
- first establishes the statements: each one is an exhaustive or near-exhaustive
5
- audit of a definition, a lemma or a proposition, and none of them runs the full
6
- instance; it covers the host family H_A for every word width from 3 to 12, the
7
- fan-in-two rewriting, and the amplified host N_3 of Section 6.3. The second
8
- executes every construction end to end: the instances on the integer reference,
9
- four machines on the netlist that sigma denotes, the hierarchy, the constructor
10
- inside the interpreter, the host on the evaluator in C, which runs three
11
- generations of the self-reproducing instance, the translating organism on the
12
- two-head machine and on its netlist in C, and the Rocq development, which needs
13
- rocq and ocamlfind on the PATH.
14
-
15
- The third runs the complete instance on the threshold evaluators, the
16
- perturbation experiments, the orbit certificate, and the amplified host: its
17
- exact self-reproduction on the evaluator in C, the self-reproducing instance of
18
- the host on it under read noise, its deviation curve split by layer, and the
19
- correction of single wrong copies along a noisy run. Its cost belongs to
20
- the object: one generation is 1,664,939 steps of a map of 41,967 units, and
21
- the amplified host reproduces itself in 33,796,449 steps of 125,901 units.
22
- The rate of each evaluator is in paper/runs/paper_throughput.json, so the cost
23
- of any one line below is a multiplication.
24
-
25
- python src/verify.py # the statements
26
- python src/verify.py --tier 2 # and every construction, end to end
27
- python src/verify.py --tier 3 # and the full-scale runs
28
- """
29
- from __future__ import annotations
30
-
31
- import argparse
32
- import concurrent.futures as cf
33
- import json
34
- import os
35
- import subprocess
36
- import sys
37
- import time
38
-
39
- REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
40
- SRC = os.path.join(REPO, "src")
41
- PY = sys.executable
42
-
43
- TIER1 = [
44
- ("check_sigma.py", [], "the canonical serialization, Definition 2.10"),
45
- ("check_device.py", [], "the device, Definition 2.8"),
46
- ("check_framing.py", [], "the framing, Definition 2.12"),
47
- ("check_host.py", [], "the machine, Definition 3.1"),
48
- ("check_lev.py", [], "the levelization, Lemma 2.6"),
49
- ("check_ternarity.py", [], "ternary depth, Proposition 3.4"),
50
- ("check_codec.py", [], "the recipe codec, Lemma 4.4"),
51
- ("check_optimal.py", [], "optimality of the bound, Proposition 4.3"),
52
- ("check_steps.py", [], "the step counts, Theorem 4.6 and Proposition 5.4"),
53
- ("check_environment.py", [], "the environment, Proposition 2.15"),
54
- ("check_interp.py", [], "the interpreter netlist, Construction 9.2"),
55
- ("check_host_family.py", [], "the host family H_A against SUBLEQ_A, Remark 3.10"),
56
- ("check_host_fold.py", [], "the folded host family, depth 11, Remark 3.11"),
57
- ("fanin2.py", [], "the fan-in-two rewriting, Corollary 9.6"),
58
- ("redundant.py", ["build"], "the amplified host N_3, Construction 6.12"),
59
- ("twohead_net.py", ["check"], "one step of the two-head netlist, Theorem 7.2"),
60
- ]
61
-
62
- TIER2 = [
63
- ("hosted_constructor.py", [], "the constructor inside the interpreter, Theorem 9.10"),
64
- ("hierarchy.py", [], "a larger interpreter runs a smaller one, Proposition 9.8"),
65
- ("check_family_gate.py", [], "four machines on the netlist of sigma"),
66
- ("check_c.py", [], "the host on the evaluator in C"),
67
- ("paper_runs.py", ["reference"], "the constructions on the integer reference"),
68
- ("twohead.py", ["run", "--generations", "3"], "the translating organism, three generations, Theorem 8.6"),
69
- ("twohead_net.py", ["lockstep", "3"], "the organism on the two-head netlist in C, three generations"),
70
- ("check_rocq.py", [], "the Rocq development, its assumptions and the extracted netlist"),
71
- ]
72
-
73
- TIER3 = [
74
- ("paper_verify.py", [], "the codec over the whole artifact, 971 MB"),
75
- ("check_family.py", [], "all 29 machines on the integer reference"),
76
- ("paper_runs.py", ["lockstep", "--device", "cuda"],
77
- "four evaluators in lockstep"),
78
- ("paper_runs.py", ["net", "--device", "cuda"],
79
- "three generations on the netlist of sigma"),
80
- ("paper_runs.py", ["lev", "--device", "cuda", "--generations", "1"],
81
- "one generation on Lev(N_host)"),
82
- ("throughput.py", [], "the rate of each evaluator"),
83
- ("noise.py", ["margin"], "the margin along the trajectory, Theorem 6.2"),
84
- ("noise.py", ["curve"], "the deviation rate against the union bound"),
85
- ("noise.py", ["leak", "--steps", "10000"],
86
- "static weight error with the zeros leaking"),
87
- ("noise.py", ["omega"], "the instance under read noise, to completion"),
88
- ("noise.py", ["profile"], "the pre-activation profile and its bound"),
89
- ("noise.py", ["certify"], "the active-input counts along the orbits, Proposition 6.4"),
90
- ("redundant.py", ["selfrep"], "exact self-reproduction of N_3 on the evaluator in C"),
91
- ("redundant.py", ["omega"], "Omega* on N_3 under read noise, to completion"),
92
- ("redundant.py", ["curve"], "the deviation rate of N_3 against the bounds"),
93
- ("redundant.py", ["restore", "--batch", "64", "--steps", "20000"],
94
- "the failure rate of the state of N_3 read by majority"),
95
- ("redundant.py", ["bound"], "the restoration bound of N_3"),
96
- ("redundant.py", ["layers"], "the profile bound of N_3 split by layer"),
97
- ]
98
-
99
-
100
- def run(entry, quiet, threads=None):
101
- name, args, what = entry
102
- t0 = time.perf_counter()
103
- env = dict(os.environ)
104
- if threads:
105
- env["OMP_NUM_THREADS"] = str(threads)
106
- p = subprocess.run([PY, os.path.join(SRC, name)] + args,
107
- capture_output=True, text=True, cwd=REPO, env=env)
108
- dt = time.perf_counter() - t0
109
- ok = p.returncode == 0
110
- label = f"{name} {' '.join(args)}".strip()
111
- print(f" {'ok ' if ok else 'FAIL'} {label:<34} {dt:7.1f} s {what}",
112
- flush=True)
113
- if not ok and not quiet:
114
- print(p.stdout[-1500:])
115
- print(p.stderr[-1500:])
116
- return ok, dt
117
-
118
-
119
- def main() -> int:
120
- ap = argparse.ArgumentParser()
121
- ap.add_argument("--tier", type=int, default=1)
122
- ap.add_argument("--quiet", action="store_true")
123
- ap.add_argument("--jobs", type=int, default=os.cpu_count() or 1,
124
- help="checks of a tier run concurrently")
125
- args = ap.parse_args()
126
-
127
- plan = [("statements", TIER1)]
128
- if args.tier >= 2:
129
- plan.append(("constructions", TIER2))
130
- if args.tier >= 3:
131
- plan.append(("full scale", TIER3))
132
-
133
- total = 0.0
134
- bad = 0
135
- record = {}
136
- for title, entries in plan:
137
- print(f"[{title}]", flush=True)
138
- t0 = time.perf_counter()
139
- jobs = max(1, min(args.jobs, len(entries)))
140
- threads = max(1, (os.cpu_count() or 1) // jobs)
141
- with cf.ThreadPoolExecutor(max_workers=jobs) as ex:
142
- futs = {ex.submit(run, e, args.quiet, threads): e for e in entries}
143
- for f in cf.as_completed(futs):
144
- e = futs[f]
145
- ok, dt = f.result()
146
- bad += not ok
147
- record[f"{e[0]} {' '.join(e[1])}".strip()] = {"ok": ok, "seconds": dt}
148
- sub = time.perf_counter() - t0
149
- total += sub
150
- print(f" {title}: {sub / 60:.1f} min wall time", flush=True)
151
- print(f"total {total / 60:.1f} min; failures: {bad}")
152
- d = os.path.join(REPO, "paper", "runs")
153
- os.makedirs(d, exist_ok=True)
154
- json.dump({"tier": args.tier, "total_seconds": total, "failures": bad,
155
- "checks": record},
156
- open(os.path.join(d, "paper_verify_times.json"), "w"), indent=1)
157
- return 0 if bad == 0 else 1
158
-
159
-
160
- if __name__ == "__main__":
161
- sys.exit(main())