From 16feea078f5542964ae85ca1b1f35bb94314ae78 Mon Sep 17 00:00:00 2001 From: DreamingOfClouds Date: Fri, 16 Oct 2026 15:02:07 -0500 Subject: [PATCH] Non-dense fitting B: E8 terms with x.y != 0 have rank >= 9; square root-code floors E8 72, P112 64, P104 56, D4 12 (no kappa claim) Lemmas 6.1-6.3 with proofs, Theorem 6 (4022 types, two orbit implementations, two CNF writers, DRAT), sharp decoders, per-term counterexamples (E8 orthogonal, D6, E7). Co-Authored-By: Claude Opus 5.5 --- .../nondense-pairing-square-floors/LICENSE | 201 +++ .../MANIFEST.sha256 | 28 + .../nondense-pairing-square-floors/NOTICE.md | 7 + .../PROOF_MAP.md | 18 + .../nondense-pairing-square-floors/README.md | 42 + .../checks/brute_agree.py | 23 + .../checks/compare_orbits.py | 14 + .../checks/counter_e8orth.py | 16 + .../checks/decoders.py | 87 + .../checks/lib.py | 40 + .../checks/manifest_check.py | 7 + .../checks/orbits_a.py | 80 + .../checks/orbits_b.py | 105 ++ .../checks/satA.py | 52 + .../checks/satB.py | 85 + .../checks/small.py | 75 + .../checks/solve_types.py | 27 + .../requirements.txt | 4 + .../vendor/drat-trim/LICENSE | 17 + .../vendor/drat-trim/drat-trim.c | 1501 +++++++++++++++++ .../nondense-pairing-square-floors/verify.sh | 59 + 21 files changed, 2488 insertions(+) create mode 100644 research/nondense-pairing-square-floors/LICENSE create mode 100644 research/nondense-pairing-square-floors/MANIFEST.sha256 create mode 100644 research/nondense-pairing-square-floors/NOTICE.md create mode 100644 research/nondense-pairing-square-floors/PROOF_MAP.md create mode 100644 research/nondense-pairing-square-floors/README.md create mode 100644 research/nondense-pairing-square-floors/checks/brute_agree.py create mode 100644 research/nondense-pairing-square-floors/checks/compare_orbits.py create mode 100644 research/nondense-pairing-square-floors/checks/counter_e8orth.py create mode 100644 research/nondense-pairing-square-floors/checks/decoders.py create mode 100644 research/nondense-pairing-square-floors/checks/lib.py create mode 100644 research/nondense-pairing-square-floors/checks/manifest_check.py create mode 100644 research/nondense-pairing-square-floors/checks/orbits_a.py create mode 100644 research/nondense-pairing-square-floors/checks/orbits_b.py create mode 100644 research/nondense-pairing-square-floors/checks/satA.py create mode 100644 research/nondense-pairing-square-floors/checks/satB.py create mode 100644 research/nondense-pairing-square-floors/checks/small.py create mode 100644 research/nondense-pairing-square-floors/checks/solve_types.py create mode 100644 research/nondense-pairing-square-floors/requirements.txt create mode 100644 research/nondense-pairing-square-floors/vendor/drat-trim/LICENSE create mode 100644 research/nondense-pairing-square-floors/vendor/drat-trim/drat-trim.c create mode 100644 research/nondense-pairing-square-floors/verify.sh diff --git a/research/nondense-pairing-square-floors/LICENSE b/research/nondense-pairing-square-floors/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/research/nondense-pairing-square-floors/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/research/nondense-pairing-square-floors/MANIFEST.sha256 b/research/nondense-pairing-square-floors/MANIFEST.sha256 new file mode 100644 index 0000000000..61a1752257 --- /dev/null +++ b/research/nondense-pairing-square-floors/MANIFEST.sha256 @@ -0,0 +1,28 @@ +53232a5fb838466d0b5301b5fff23459075eaff418eff4928bf0518c7d1521d2 checks/brute_agree.py +99b3cabe25ce291b30afbdaf1324bf49a51ef544e3204836766caf561488cd9b checks/compare_orbits.py +e1c43c4b030b2cebd0d7e1485b6762cdbee7031b4fe5dba209332000c039f73c checks/counter_e8orth.py +f21fabb3292885bbcc9d4b5f4dca94f9d3c74b884fb7ebd487b801b630ca5b0e checks/decoders.py +e5879757a5a84893a9e98ec738d1408ad0a95a171eddecd0211ff09134befe6f checks/lib.py +35a40b0341b4e8824526642f12d4b2521984a242e7f23911149542116dcf6b8b checks/manifest_check.py +634bb4bfde734f0eaedb15ece60d02a31dc7f7137ec01cf99cc9e47db5a6e6dd checks/orbits_a.py +2ddd99cc39ecc5643ea1132ca7d713095f58547bbbfab9f10a3d3551260820f0 checks/orbits_b.py +046fd81ce21b3a55057029dc2fe55b06a788729bdb09e6f2e10f0294223ec328 checks/satA.py +9721991a505cef90254583c02425227005c28cc967f27cc4e7c44db8b5111860 checks/satB.py +21a3b0eede97f9fce3e8626b9399f11d5e436c784c59da0dc71c5155bac19276 checks/small.py +d4cd2a002a5f4548bff47de8367de752ad6539796b5667604e3900fbd9aa83eb checks/solve_types.py +745ab126c9b1eb384449ed5bd53e57c72944f1265ec5cf979bfa10dd681b88b4 evidence/a1_brute.log +058cd53de1f9acc3b54f327ad78abda7d50ef0bd37f68d0b98e958ef6ecf7d58 evidence/a1_e8.log +c7977a705597449965a6b159c22b7264fbae51a1ceb5a9b12ba0c79d24de5b69 evidence/a1_orth.log +985a73e096266f33e170b351dcb80555ca1396efa8619dc756d18149aa65ba0e evidence/a1_small.log +73e9ff5f31a175fc08700d87d55f7cc97ff909a7f0aa5f56fe2c8e410be4b51d evidence/cert_non.log +aeb566be0288f911fffd9f02d292637a230ef0aa8b2bbdd77c1f130d02d51087 evidence/gen_E7.log +3d07c9a8346fdafccb0c717c194e1307a167959a3744b93f981a14f5c5de3c54 evidence/gen_E8.log +88aefced1f21a6be0ba29c356e5e2302a71280bc84119a77e4c49b212be1415d evidence/verify_FULL.log +c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4 LICENSE +486e0cfbb6fa50b0ca0cdb6e7d4db92833cd6033365faa2e382a3937d548e370 NOTICE.md +392b29a8c4616c23eedbddf847d7b84cfa5ca63f94b733093d0714f59ae12972 PROOF_MAP.md +328780fc5cdef9ceccb122a4cd196a3195fc028f7c9360f23899b4b8dc2601ad README.md +cc35e5727fd297b7ade4026eb8bf209d8e5573859da224c25813375de6f5c0a4 requirements.txt +d834b649f437e091597f5347f259b9f681087f89ca0844d0cee250a1a1a0c2ee vendor/drat-trim/drat-trim.c +71ab2a9dc5a294ad8fba4ccb8c45aaa8a439596d314a2a046ac9b62266872a18 vendor/drat-trim/LICENSE +c27f6c65306226847266c8b9da4d8655a94106cc09a45ba2736a3ba0fc344c48 verify.sh diff --git a/research/nondense-pairing-square-floors/NOTICE.md b/research/nondense-pairing-square-floors/NOTICE.md new file mode 100644 index 0000000000..88587670f8 --- /dev/null +++ b/research/nondense-pairing-square-floors/NOTICE.md @@ -0,0 +1,7 @@ +# Notice + +Prepared by DreamingOfClouds with Claude Opus 5.5 (Anthropic), via Claude Code; Apache-2.0 (`LICENSE`, copied from the repository root). +`vendor/drat-trim/` is Marijn Heule's drat-trim (`drat-trim.c`, its `LICENSE`), the same pinned source as `research/p111-minrank-proof-reconciliation/vendor/drat-trim/`. + +## Credits +**#968** (frame-pair P104 cost-56 decoder; the 8/7 rule), **#1744** (E8 / P112 / P104 floors in their scopes), **#2176** (forced triangular minors; they cap at 7 per co-flat term and cannot close non-dense B), **#2338** (Theorems P and R, the pairing/CNF framework this note's `B` is the fitting matrix of), and `research/ideal-decoder-floors/` (Theorem 4, the dense-pairing root-code floor T·r_min that this note extends to non-dense B). None of their files is copied. diff --git a/research/nondense-pairing-square-floors/PROOF_MAP.md b/research/nondense-pairing-square-floors/PROOF_MAP.md new file mode 100644 index 0000000000..71bde33b72 --- /dev/null +++ b/research/nondense-pairing-square-floors/PROOF_MAP.md @@ -0,0 +1,18 @@ +# PROOF_MAP: claim -> check (anything not listed is CONDITIONAL or OPEN in the README) +| Claim | Status | Check (all run by `verify.sh`; log names in `OUT/logs/`) | +|---|---|---| +| Lemma 6.1 (per-term fitting minrank = mr2 on R∩A) | PROVEN (README §1) | exercised on every `mr2` in `brute_agree`, `d4`, `e7`, `d6`, `e8orth` | +| Lemma 6.2 (normal-pair reduction) | PROVEN (README §1) | consumed by Theorem 6's type enumeration (`orbA`, `orbB`, `cmp`) | +| Lemma 6.3 (orthogonal-set deletion) | PROVEN (README §1) | consumed by Cor 6.4 (P112/P104 scopes); no separate script | +| 440 880 hyperplanes, 4022 non-orthogonal + 318 orthogonal types, min \|D\| = 30 | CHECKED, two implementations | `orbA`, `orbB`, `cmp` (`checks/orbits_a.py`, `orbits_b.py`, `compare_orbits.py`) | +| CNF writers agree with exhaustive minrank over all fitting B | CHECKED | `brute` (`brute_agree.py`: 60 instances, both writers, DRAT + decoded SAT models) | +| Theorem 6: all 4022 types UNSAT at rank <= 8, DRAT VERIFIED | CHECKED | FULL=1 steps `FULL writer A`, `FULL writer B` (`solve_types.py ... chunk:i/4`); default: fixed 32-type sample `solA`, `solB` (14 smallest \|D\| plus 18 spread) | +| E8 >= 72 (every term x·y≠0 has rank >= 9, any fitting B) | PROVEN given Theorem 6 | as above | +| P112 >= 64, P104 >= 56 (floor T·r_min, not per-term dense value) | PROVEN given Theorem 6 + Lemma 6.3 | as above; frames asserted in `dec` | +| D4 >= 12 (192 D-sets, mr2 >= 3) | CHECKED | `d4` (`small.py D4`) | +| Sharp: E8 72, P112 64, P104 56 exact decoders | CHECKED | `dec` (`decoders.py`) | +| E8 orthogonal normals: \|D\| = 24, mr2 = 6 (dense block 9) | CHECKED | `e8orth` (`counter_e8orth.py`) | +| D6: \|D\|=7 mr2=3; \|D\|=10,15,16,19 mr2<=4 (scan \|D\|<=19 only) | CHECKED (default: one witness per size; FULL: complete scan, 240 sets of \|D\|=7) | `d6` (`small.py D6 [full]`) | +| E7: \|D\| = 11 type mr2 = 5 | CHECKED | `e7` (`small.py E7`) | +| No decoder below any floor is shown | SCOPE STATEMENT | README §4 | +| Overcomplete (48 types at 8, 19 undecided), E7/D6 square, gap (ii), D6 shared-B test | OPEN / CONDITIONAL | not checked | diff --git a/research/nondense-pairing-square-floors/README.md b/research/nondense-pairing-square-floors/README.md new file mode 100644 index 0000000000..23f8dcc1f2 --- /dev/null +++ b/research/nondense-pairing-square-floors/README.md @@ -0,0 +1,42 @@ +# Non-dense fitting B: per-term rank ≥ 9 for every E8 term with x·y ≠ 0, and the square root-code floors E8 ≥ 72, P112 ≥ 64, P104 ≥ 56, D4 ≥ 12 for any fitting B (no κ claim) + +> **Scope, stated first.** A research note in the line of `research/ideal-decoder-floors/` (Theorem 4, public: the floor T·r_min for the *dense* pairing), #968, #1744, #2176 and #2338 (Theorems P, R). It removes "dense" from the floor in the square case. **No κ claim, no window, no supplier word, no decoder below any known floor.** Only `research/nondense-pairing-square-floors/` is added. +> Labels: **PROVEN** (written proof here, with the `verify.sh` step for each numerical input), **CHECKED** (an exact script that `verify.sh` re-runs and asserts, DRAT proofs replayed with the vendored drat-trim), **CONDITIONAL**, **OPEN**. Every claim is in `PROOF_MAP.md` with the command that checks it. +> Overlap, honestly: #1744 already gives rank ≥ 9 for any fitting B on P111/P112 in its scope. What is added here is the *per-term* statement on the whole E8 root unit via root-spanned hyperplane pairs, the proof structure (Lemmas 6.1-6.3), the P104 and D4 corollaries, two independent implementations, and the explicit list of where the per-term claim is false. + +## 0. Setting +Unit: `v` root lines `t` (E8: 120). A decoder `(T, P, Q, B)` has `P = D₁RX`, `Q = D₂RY` (root-code factors: columns `x_j, y_j` of `X, Y`, `R` the root matrix), `R(x) = {t : r_t·x ≠ 0}`, `R_j = R(x_j)`, `A_j = R(y_j)`, `D_j = R_j ∩ A_j`. `B ∈ F2^{v×v}` is **fitting** if `B_tt = 1` and `B_ts = 0` whenever `r_t·r_s ≠ 0` (t ≠ s); entries on orthogonal pairs are free (the dense pairing sets them to 1). cost = Σ_j rank_{F2} B[R_j, A_j]. For `D ⊆ lines`, `G⊥[D]` is the orthogonality graph and `mr₂(G⊥[D])` the minimum F2 rank of a fitting matrix on `D` (fitting minrank). + +## 1. Lemmas 6.1-6.3 (PROVEN) +**Lemma 6.1 (per-term reduction).** `min_{fitting B} rank B[R, A] = mr₂(G⊥[R ∩ A])`. +*Proof.* (≥) `B[D,D]`, `D = R∩A`, is a submatrix of `B[R,A]` and is fitting on `D`. (≤) Every entry of `B[R,A]` outside `D × D` sits at `(t,s)` with `t ≠ s` (if `t = s` then `t ∈ R ∩ A = D`), so it is forced to 0 or free; set all of them to 0 and put an optimal fitting matrix on `D × D`: the block is `B_D ⊕ 0`-padded, of rank `rank B_D`. ∎ (Per term only: a decoder shares one `B` across all terms.) + +**Lemma 6.2 (normal-pair reduction).** If `x·y ≠ 0` then `R(x) ∩ R(y) ⊇ R(n₁) ∩ R(n₂)` for root-spanned hyperplanes `n₁^⊥ ⊇ F_x := x^⊥ ∩ Φ`, `n₂^⊥ ⊇ F_y` with `n₁·n₂ ≠ 0`. +*Proof.* `S = span F_x`. Extend the images of the roots in `V/S` to a basis of `V/S` consisting of roots; deleting one basis root at a time gives hyperplanes `S + span(b_l, l ≠ i)`, each spanned by roots, with intersection `S`; their normals span `S^⊥`. Likewise `S' = span F_y`. Now `x ∈ S^⊥`, `y ∈ S'^⊥` and `x·y ≠ 0`, so the two normal sets are not mutually orthogonal: some `n₁·n₂ ≠ 0`. Since `F_x ⊆ n₁^⊥`, every root non-orthogonal to `n₁` is non-orthogonal to `x`: `R(x) ⊇ R(n₁)`; same for `y`. ∎ + +**Lemma 6.3 (orthogonal-set deletion).** If `S ⊆ D` is pairwise orthogonal then `mr₂(D \ S) ≥ mr₂(D) − 1`. +*Proof.* `S` is a clique of `G⊥`, so the all-ones `J_S` is fitting on `S`. If `B'` is fitting on `D \ S` then `B' ⊕ J_S` (zero cross blocks, allowed since cross entries of distinct lines are forced-0-or-free and 0 is admissible) is fitting on `D` with rank `rank B' + 1`. ∎ (Per pairwise-orthogonal set, not per line.) + +## 2. Theorem 6 (CHECKED, kissat + drat-trim) +**E8.** Let `n₁` range over the 8 W-orbits of fundamental coweights ω_j (together the 440 880 root-spanned hyperplanes), and `n₂` over the orbits of `Stab(ω_j) = W_J` (Steinberg: the parabolic generated by the simple reflections ≠ j) on hyperplane normals with `n₁·n₂ ≠ 0`. There are **4022** such types (76 + 214 + 485 + 1610 + 1009 + 454 + 145 + 29; 318 orthogonal types are not used), with `|D| = |R(n₁) ∩ R(n₂)| ≥ 30`. For each, the CNF "a fitting matrix of F2-rank ≤ 8 exists on `G⊥[D]`" is **UNSAT** with a drat-trim VERIFIED proof. (The CNF is normalised by a pairwise non-orthogonal 8-set `S ⊆ D`: fitting forces `B[S,S] = I`, so after a change of basis `U_S = V_S = I`, and `U_t[k] = B[t,S_k]`, `V_s[k] = B[S_k,s]`; non-orthogonal entries are 0, orthogonal ones free, diagonal 1, with Tseitin AND/XOR clauses. A missing 8-clique would be reported `noS`, counted as a failure.) Using the `W_J`-orbit rather than the orbit of the *pair* is finer, hence conservative. +**Consequence (6.1 + 6.2 + monotonicity of mr₂ under inclusion of D).** Every E8 term with `x·y ≠ 0` has rank ≥ 9 for every fitting B. (Monotonicity: `D ⊇ D₀` gives `mr₂(D) ≥ mr₂(D₀)`, a principal submatrix.) +Hyperplane count: `Σ_J |W|/|W_J| / 2 = 440 880` (stabiliser orders 2160 + 17280 + 69120 + 483840 + 241920 + 60480 + 6720 + 240 = 881 760, halved). +**Checks.** Two implementations of the orbit enumeration (`checks/orbits_a.py`: the IDEAL code, hyperplane normals as W-orbits of coweights, union-find over 7 simple reflections; `checks/orbits_b.py`: the finisher's code, ambient integer coordinates, all 120 reflections, scipy connected components) and two CNF writers (`checks/satA.py`; `checks/satB.py`, chunked direct XOR, own clique routine and SAT-model decoder); `compare_orbits.py` asserts identical per-type invariants; `brute_agree.py` asserts exact agreement of exhaustive minrank over all fitting B with both writers on 60 instances. + +## 3. Corollary 6.4: square root-code decoders, any fitting B (PROVEN given Theorem 6; sharp) +Square means `m = T` and `X Yᵀ = I`, so `y_j·x_j = 1 ≠ 0` for every `j`. Hence every term has rank ≥ 9 on E8; on a live set obtained by deleting lines the same bound holds with `D` replaced by `D` minus the deleted lines, and Lemma 6.3 removes at most 1 per pairwise-orthogonal deleted set: +* **E8 ≥ 72** = 8·9. **P112 ≥ 64** (E8 minus one 8-frame: ≥ 8 per term) and **P104 ≥ 56** (E8 minus two disjoint orthogonal 8-frames: ≥ 7 per term); T ≥ 8 terms are needed (place bound of `ideal-decoder-floors`, Theorem 1). *Scope:* this proves the floor `T·r_min`; it does **not** say each live term attains its own dense value. +* **D4 ≥ 12**: all 24 root-spanned hyperplane normals of D4, all non-orthogonal pairs: 192 distinct `D`-sets, each with `mr₂ ≥ 3` (brute force over all fitting B on 180, SAT at rank 2 UNSAT with DRAT on 12), 4 terms × 3. +* **Sharp** (exact decoders, `checks/decoders.py`): E8 coordinate decoder `X = Y = I`, Gram exact, term ranks 9×8 = 72; P112: `x_j = y_j` = the 8 lines of the removed frame, `D Tᵀ = 2G` exact on the 112 live lines, ranks 8×8 = 64; P104: the frame-pair decoder of #968 rebuilt (`build56` part of `decoders.py`), `D Tᵀ = 2G` exact on the 104 live lines, 8 terms of rank 7 = 56, dead lines = two disjoint pairwise-orthogonal 8-frames (asserted). + +## 4. Where the per-term (block) claim is FALSE (CHECKED; Lemma 6.1 B is `0` outside `D×D`, optimal on `D×D`) +* **E8, orthogonal normals α ⊥ β:** `|D| = 24`, `mr₂ = 6` exactly (rank 6 SAT, model decoded; rank 5 UNSAT with DRAT, both writers), while the dense block value is 9 (`counter_e8orth.py`). Such a term needs `x ⊥ y`, i.e. only an overcomplete decoder (trace Σ x_j·y_j = T) can use it. +* **D6, non-orthogonal pairs:** `|D| = 7` types with `mr₂ = 3` (240 sets, brute force) and sets with `|D| = 10, 15, 16, 19` and `mr₂ ≤ 4` (SAT, decoded and re-verified as fitting). **The scan covers only `|D| ≤ 19`** (26 096 distinct non-orthogonal D-sets in total). FULL=1 runs the whole `|D| ≤ 19` scan. +* **E7:** the type `n₁` = E6-flat (36 lines orthogonal), `|D| = 11`: `mr₂ = 5` (rank 5 SAT, rank 4 UNSAT with DRAT), 756 pairs, 378 distinct `D`. +* **No decoder below a floor is shown or claimed.** These are per-term counterexamples only; a decoder shares one `B` across all its terms. For D6, a shared-`B` test (one of the earlier runs, not part of `verify.sh`, **CONDITIONAL**) found that the shared `B` blocked the saving on one tested allocation. + +## 5. Open (not claimed) +(1) Overcomplete E8 / P112 / P104 decoders using terms with `x_j ⊥ y_j`: one E8 type has `mr₂ = 6`, 48 types have value 8 on `D×D`, 19 types are undecided (the previous exploration's numbers; **CONDITIONAL**: not recomputed by `verify.sh`, only the `mr₂ = 6` type is). Needs a coupling lemma or a global-`B` argument. (2) E7 and D6 square: global-`B` coupling needed. (3) Factors outside the root code (gap (ii)). (4) The pair-orbit (rather than `W_J`-orbit) count is not used. + +## 6. Reproduce +`bash verify.sh [OUTDIR]` (default, a few minutes, ≤ 2 processes) or `FULL=1 bash verify.sh` (all 4022 types re-solved by both CNF writers with every proof drat-trim VERIFIED, four chunks at a time, plus the whole D6 scan). Requires python3 with numpy, scipy, sympy; `kissat`; a C compiler. `evidence/` holds the historical logs of the original full run (not used by `verify.sh`, which recomputes). DRAT proofs are not shipped (about 34 KB each, ~135 MB for 4022); FULL regenerates and replays them. diff --git a/research/nondense-pairing-square-floors/checks/brute_agree.py b/research/nondense-pairing-square-floors/checks/brute_agree.py new file mode 100644 index 0000000000..1ffddbabdb --- /dev/null +++ b/research/nondense-pairing-square-floors/checks/brute_agree.py @@ -0,0 +1,23 @@ +# 60 random small E8 line subsets (|D| 6-10): exhaustive brute-force minrank over ALL fitting B (free entries = orthogonal pairs) +# must equal the SAT threshold of BOTH CNF writers (SAT at mr, UNSAT at mr-1, DRAT-verified; SAT models decoded and re-checked as fitting). +import random, sys +from lib import * +import satA, satB +L=lines(e8_roots()); G=[[int(dot(a,b)) for b in L] for a in L] +rnd=random.Random(7); done=0; bad=0; seen=set() +for _ in range(4000): + if done>=60: break + D=sorted(rnd.sample(range(120),rnd.randint(6,10))); key=tuple(D) + m=satB.brute_mr(G,D,cap=20) + if m is None or key in seen or m<2: continue + S1=nonorth_clique(G,D,m); S0=nonorth_clique(G,D,m-1) + if S1 is None or S0 is None: continue + seen.add(key) + a=satB.solve(G,D,m,S1); b=satB.solve(G,D,m-1,S0) + ta=satA.run(G,D,m,S1,tag='a1'); tb=satA.run(G,D,m-1,S0,tag='a0') + ok=(a==('SAT',True) and b==('UNSAT',True) and ta[0]=='SAT' and tb[:2]==('UNSAT',True)) + bad+=not ok; done+=1 + print(len(D),'brute',m,'writerB',a,b,'writerA',ta[0],tb[:2],'OK' if ok else 'MISMATCH',flush=True) +print('BRUTE-vs-CNF instances',done,'mismatches',bad) +print('PASS brute force = both CNF writers on 60 instances' if done==60 and bad==0 else 'FAIL brute-vs-CNF') +sys.exit(0 if done==60 and bad==0 else 1) diff --git a/research/nondense-pairing-square-floors/checks/compare_orbits.py b/research/nondense-pairing-square-floors/checks/compare_orbits.py new file mode 100644 index 0000000000..c4cb6580d1 --- /dev/null +++ b/research/nondense-pairing-square-floors/checks/compare_orbits.py @@ -0,0 +1,14 @@ +# Agreement of the two orbit implementations: same hyperplane/type counts and the same multiset of invariants per type. +import sys, json, gzip +from collections import Counter +def inv(path): + T=json.load(gzip.open(path,'rt')) + return Counter((t['j'],t['nonorth'],len(t['D']),tuple(sorted(bin(r).count('1') for r in t['orth']))) for t in T), T +a,Ta=inv(sys.argv[1]); b,Tb=inv(sys.argv[2]) +na=sum(t['nonorth'] for t in Ta); nb=sum(t['nonorth'] for t in Tb) +print('implementation A: types',len(Ta),'nonorth',na,'orth',len(Ta)-na,'min|D| nonorth',min(len(t['D']) for t in Ta if t['nonorth'])) +print('implementation B: types',len(Tb),'nonorth',nb,'orth',len(Tb)-nb,'min|D| nonorth',min(len(t['D']) for t in Tb if t['nonorth'])) +ok=(a==b and na==nb==4022 and len(Ta)-na==318 and min(len(t['D']) for t in Ta if t['nonorth'])==30) +print('per-type invariant multisets (node class, orthogonal?, |D|, orthogonality degree sequence) identical:',a==b) +print('PASS orbit recount: 4022 nonorth types, 318 orth types, min |D| = 30, both implementations' if ok else 'FAIL orbit recount') +sys.exit(0 if ok else 1) diff --git a/research/nondense-pairing-square-floors/checks/counter_e8orth.py b/research/nondense-pairing-square-floors/checks/counter_e8orth.py new file mode 100644 index 0000000000..0df692e47b --- /dev/null +++ b/research/nondense-pairing-square-floors/checks/counter_e8orth.py @@ -0,0 +1,16 @@ +# E8 orthogonal root normals alpha _|_ beta: D = lines non-orthogonal to both, |D| = 24, dense rank 9 (dense block value), mr2 = 6 exactly. +import sys +from lib import * +import satA, satB +L=lines(e8_roots()); G=[[int(dot(a,b)) for b in L] for a in L] +a=0; b=next(t for t in range(120) if G[a][t]==0) +D=[t for t in range(120) if G[a][t]!=0 and G[b][t]!=0] +dr=dense_rank(G,D) +R=[t for t in range(120) if G[a][t]!=0]; Ab=[t for t in range(120) if G[b][t]!=0] +block=rank2([sum(1<j and G[j][k]==0]) +rec([],pool) +print("frames",len(frames)) +def sols(F1): + out=[] + for sm in range(256): + S=[F0[j] for j in range(8) if sm>>j&1]; need=8-len(S) + part={f:[g for g in F1 if [x for x in S if G[g][x]]==[f]] for f in S} + if any(not v for v in part.values()): continue + for ch in itertools.product(*[part[f] for f in S]): + if len(set(ch))!=len(S): continue + Ts=[g for g in F1 if g not in ch] + if len(Ts)!=need: continue + rest=[f for f in F0 if f not in S] + p2={g:[f for f in rest if [x for x in Ts if G[f][x]]==[g]] for g in Ts} + if any(not v for v in p2.values()): continue + for ch2 in itertools.product(*[p2[g] for g in Ts]): + if len(set(ch2))!=len(Ts): continue + out.append(list(zip(S,ch))+list(zip(Ts,ch2))) + return out +def cost(A,Bc,dead): + live=[t for t in range(N) if t not in dead]; rs=[] + for a,b in zip(A,Bc): + R=[t for t in live if dot(L[t],a)!=0]; C=[t for t in live if dot(L[t],b)!=0] + rs.append(rank_f2([sum(1< (w2.a2)/4 = w.a ; w2 = 2w + # new w2 = w2 - 2*(w.a)/ (a.a) * 2a/... : w - (w.a) a -> w2 - (w.a) a2 + return tuple(x-d*y for x,y in zip(w,a2)) +allN=[]; typ=[] +for j in range(8): + w=tuple(int(2*Wt[i,j]) for i in range(8)) # 2*omega_j (check integrality) + assert all(2*Wt[i,j]==int(2*Wt[i,j]) for i in range(8)) + seen={w}; fr=[w] + while fr: + nf=[] + for u in fr: + for a in S2: + z=refl(u,a) + if z not in seen: seen.add(z); nf.append(z) + fr=nf + # keep one of +- + rep=[u for u in seen if tuple(-x for x in u) not in seen or u>tuple(-x for x in u)] + print(j+1,len(seen),len(rep),file=sys.stderr) + allN+=rep; typ+=[j]*len(rep) +N=np.array(allN,dtype=np.int64) +dots=(N@(2*Lm).T.round().astype(np.int64)) # 4 * n.l +masks=[] +for row in dots: + m=0 + for k in np.nonzero(row)[0]: m|=1<>t&1] + types.append(dict(j=j,k=k,orbit=len(mem),nonorth=nonorth,D=D,orth=[sum(1<0 else -v)) +def sgn(v): + v=np.asarray(v); nz=np.nonzero(v)[0][0]; return tuple(int(x) for x in (v if v[nz]>0 else -v)) +Lset=sorted({sgn(r) for r in R}); L=np.array(Lset,dtype=np.int64); assert len(L)==120 +G=(L@L.T)//4 # true inner products +# simple roots (Bourbaki), doubled +simple=np.array([[1,-1,-1,-1,-1,-1,-1,1],[2,2,0,0,0,0,0,0],[-2,2,0,0,0,0,0,0],[0,-2,2,0,0,0,0,0],[0,0,-2,2,0,0,0,0],[0,0,0,-2,2,0,0,0],[0,0,0,0,-2,2,0,0],[0,0,0,0,0,-2,2,0]]) +C=(simple@simple.T)//4 +assert all(C[i,i]==2 for i in range(8)) and sorted(int(x) for x in C[np.triu_indices(8,1)]).count(-1)==7 +# fundamental weights: w_j with =delta_ij ; solve exactly +def solve(A,b): + A=[[Fr(int(x)) for x in row]+[Fr(int(y))] for row,y in zip(A,b)]; n=len(A) + for c in range(n): + p=next(r for r in range(c,n) if A[r][c]!=0); A[c],A[p]=A[p],A[c] + A[c]=[x/A[c][c] for x in A[c]] + for r in range(n): + if r!=c and A[r][c]!=0: A[r]=[x-A[r][c]*y for x,y in zip(A[r],A[c])] + return [A[r][n] for r in range(n)] +W=[] +for j in range(8): + w=solve((simple//1).tolist(),[2*int(i==j) for i in range(8)]) # simple doubled: (2a_i).(w)/... use doubled dot + # simple rows are 2a_i ; (2a_i).w = 2 delta -> a_i.w = delta ; w in true coords + den=1 + for x in w: den=den*x.denominator//np.gcd(den,x.denominator) + v=[int(x*den) for x in w]; g=np.gcd.reduce(np.abs(v)); v=[x//g for x in v] + W.append(v) +Wv=np.array(W,dtype=np.int64) +assert all(((simple@Wv[j])!=0).sum()==1 for j in range(8)) +# BFS orbits of primitive integer normals under all 120 reflections: s_r(v)=v-2(v.r)/(r.r) r, r doubled -> v - (v.R)/4 * R ... keep integer by scaling +def vcanon(A): + A=np.asarray(A,dtype=np.int64).copy(); g=np.gcd.reduce(np.abs(A),axis=1); A//=g[:,None] + f=np.argmax(A!=0,axis=1); sg=np.sign(A[np.arange(len(A)),f]); A*=sg[:,None]; return A +def vkey(A): + assert np.abs(A).max()<30000 + return np.ascontiguousarray(A.astype(np.int16)).view('S16').ravel() +def orbit(v0): + cur=vcanon(np.array([v0])); seenk=np.sort(vkey(cur)); fr=cur; allv=[cur] + while len(fr): + newk=[];newv=[] + for c in range(0,len(fr),1500): + f=fr[c:c+1500]; d=f@L.T + img=vcanon((4*f[:,None,:]-d[:,:,None]*L[None,:,:]).reshape(-1,8)); k=vkey(img) + k,ii=np.unique(k,return_index=True); img=img[ii] + m=~np.isin(k,seenk); newk.append(k[m]); newv.append(img[m]) + if newk: + nk=np.concatenate(newk); nv=np.concatenate(newv); nk,ii=np.unique(nk,return_index=True); nv=nv[ii] + else: nk=seenk[:0]; nv=fr[:0] + seenk=np.sort(np.concatenate([seenk,nk])); fr=nv; allv.append(nv) + return [tuple(int(x) for x in v) for v in np.concatenate(allv)] +normals=[]; typ=[] +for j in range(8): + O=orbit(Wv[j]); normals+=O; typ+=[j]*len(O); print('type',j,'lines',len(O),file=sys.stderr,flush=True) +N=np.array(normals,dtype=np.int64); print('hyperplanes',len(N),'distinct',len(set(normals)),flush=True) +# verify each is root-spanned: rank of roots orthogonal = 7 +Mk=(N@L.T)!=0 # (H,120) True = r_t . n != 0 +for k in np.random.default_rng(1).choice(len(N),200,replace=False): + Z=L[~Mk[k]]; assert np.linalg.matrix_rank(Z.astype(float))==7 +allk=vkey(N); order=np.argsort(allk); sk=allk[order] +def idx_of(arr): + k=vkey(vcanon(arr)); p=np.searchsorted(sk,k); assert (sk[p]==k).all(); return order[p] +key={tuple(v):k for k,v in enumerate(normals)} +res={} +for j in range(8): + n1=Wv[j]; i1=key[canon(n1)] + perp=[t for t in range(120) if (L[t]@n1)==0] + rows=[];cols=[] + for t in perp: + num=N@L[t]; img=(4*N-num[:,None]*L[t][None,:]) + rows.append(np.arange(len(N))); cols.append(idx_of(img)) + rows=np.concatenate(rows); cols=np.concatenate(cols) + nc,lab=connected_components(coo_matrix((np.ones(len(rows)),(rows,cols)),shape=(len(N),len(N))),directed=False) + reps={} + for k in range(len(N)): reps.setdefault(lab[k],k) + non=[k for k in reps.values() if int(N[i1]@N[k])!=0]; orth=[k for k in reps.values() if int(N[i1]@N[k])==0] + res[j]=(i1,non,orth); print('j',j,'roots perp',len(perp),'orbits',nc,'non',len(non),'orth',len(orth),flush=True) +print('TOTAL non',sum(len(r[1]) for r in res.values()),'orth',sum(len(r[2]) for r in res.values()),flush=True) +Mk=(N@L.T)!=0 +types=[] +for j in range(8): + i1,non,orth=res[j] + for kind,lst in ((True,non),(False,orth)): + for k in lst: + D=[t for t in range(120) if Mk[i1,t] and Mk[k,t]] + types.append(dict(j=j,k=int(k),nonorth=kind,D=D,orth=[sum(1< U_S=V_S=I). Variables U_t[k]=B[t,S_k], V_s[k]=B[S_k,s]; Tseitin AND/XOR. +import subprocess, os, itertools, tempfile +from lib import nonorth_clique +KISSAT=os.environ.get('KISSAT','kissat'); DT=os.environ.get('DRAT_TRIM','drat-trim') +def cnf(G,D,r,S): + nv=[0]; cl=[] + def new(): nv[0]+=1; return nv[0] + rest=[t for t in D if t not in S] + U={t:[new() if G[t][S[k]]==0 else None for k in range(r)] for t in rest} # U_t[k]=B[t,S_k]; forced 0 if nonorth + V={s:[new() if G[S[k]][s]==0 else None for k in range(r)] for s in rest} + def lit_xor(lits,c): + # Tseitin chain: xor of lits == c + if not lits: + if c: cl.append([]) + return + acc=lits[0] + for l in lits[1:]: + z=new(); cl.extend([[-z,acc,l],[-z,-acc,-l],[z,-acc,l],[z,acc,-l]]); acc=z + cl.append([acc] if c else [-acc]) + for t in rest: + for s in rest: + if t!=s and G[t][s]==0: continue + c=1 if t==s else 0 + prods=[] + for k in range(r): + a,b=U[t][k],V[s][k] + if a is None or b is None: continue + z=new(); cl.extend([[-z,a],[-z,b],[z,-a,-b]]); prods.append(z) + lit_xor(prods,c) + return nv[0],cl +def run(G,D,r,S=None,tag='x',check=True,timeout=1200,keep=None): + if S is None: S=nonorth_clique(G,D,r) + if S is None: return ('noS',None,0,0) + n,cl=cnf(G,D,r,S) + d=tempfile.mkdtemp(); f=os.path.join(d,tag+'.cnf'); p=os.path.join(d,tag+'.drat') + with open(f,'w') as o: + o.write(f'p cnf {n} {len(cl)}\n') + for c in cl: o.write(' '.join(map(str,c))+' 0\n') + out=subprocess.run([KISSAT,'-q','--no-binary',f,p],capture_output=True,text=True,timeout=timeout).stdout + res='SAT' if 's SATISFIABLE' in out else ('UNSAT' if 'UNSATISFIABLE' in out else '?') + ver=None + if res=='UNSAT' and check: + o2=subprocess.run([DT,f,p],capture_output=True,text=True,timeout=timeout).stdout + ver='s VERIFIED' in o2 + if keep and ver: + subprocess.run(['xz','-9e','-c',p],stdout=open(os.path.join(keep,tag+'.drat.xz'),'wb')) + for x in (f,p): + try: os.remove(x) + except OSError: pass + os.rmdir(d) + return res,ver,n,len(cl) diff --git a/research/nondense-pairing-square-floors/checks/satB.py b/research/nondense-pairing-square-floors/checks/satB.py new file mode 100644 index 0000000000..68c0fb8765 --- /dev/null +++ b/research/nondense-pairing-square-floors/checks/satB.py @@ -0,0 +1,85 @@ +# AUDIT-IQ: independent minrank tools. Own clique search, own CNF (chunked direct XOR), own brute force, model decoding. +import itertools, os, subprocess, random, tempfile +KISSAT=os.environ.get('KISSAT','kissat'); DT=os.environ.get('DRAT_TRIM','drat-trim') +def rk2(rows): + b=[]; + for x in rows: + for y in b: x=min(x,x^y) + if x: b.append(x) + return len(b) +def clique(G,D,r,seed=0,tries=3000): + """random greedy search for r pairwise NON-orthogonal lines in D""" + rnd=random.Random(seed); D=list(D) + for _ in range(tries): + rnd.shuffle(D); S=[] + for t in D: + if all(G[t][s]!=0 for s in S): S.append(t) + if len(S)==r: return S + return None +def brute_mr(G,D,cap=22): + m=len(D); free=[(a,b) for a in range(m) for b in range(m) if a!=b and G[D[a]][D[b]]==0] + if len(free)>cap: return None + best=m + for bits in range(1<>q&1: rows[a]|=1< aux by direct clauses, chunk size <=3 + while len(lits)>3: + ch,lits=lits[:3],lits[3:]; a=s.v(); s.direct(ch+[a],0); lits=lits+[a] + if not lits: + if c: s.cl.append([]) + return + s.direct(lits,c) + def direct(s,lits,c): + # forbid every assignment of lits with parity != c + for bits in itertools.product((0,1),repeat=len(lits)): + if sum(bits)%2!=c: s.cl.append([(-l if b else l) for l,b in zip(lits,bits)]) +def encode(G,D,r,S): + """rank<=r fitting matrix on G_perp[D]; normalised U_S=V_S=I (B[S,S]=I forced since S pairwise non-orth)""" + f=CNF(); rest=[t for t in D if t not in S] + U={t:[(f.v() if G[t][S[k]]==0 else 0) for k in range(r)] for t in rest} + V={t:[(f.v() if G[S[k]][t]==0 else 0) for k in range(r)] for t in rest} + for t in rest: + for s in rest: + if t!=s and G[t][s]==0: continue + ps=[] + for k in range(r): + a,b=U[t][k],V[s][k] + if a and b: + p=f.v(); f.cl+= [[-p,a],[-p,b],[p,-a,-b]]; ps.append(p) + f.xor_eq(ps,1 if t==s else 0) + return f,U,V,rest +def solve(G,D,r,S=None,seed=0,drat=True,timeout=1200,verify_model=True): + if S is None: S=clique(G,D,r,seed) + if S is None: return ('noS',None) + f,U,V,rest=encode(G,D,r,S) + d=tempfile.mkdtemp(dir=None); fn=os.path.join(d,'a.cnf'); pf=os.path.join(d,'a.drat') + with open(fn,'w') as o: + o.write(f'p cnf {f.n} {len(f.cl)}\n'); o.write(''.join(' '.join(map(str,c))+' 0\n' for c in f.cl)) + p=subprocess.run([KISSAT,'--no-binary',fn,pf] if drat else [KISSAT,fn],capture_output=True,text=True,timeout=timeout) + out=p.stdout; res='SAT' if '\ns SATISFIABLE' in '\n'+out else ('UNSAT' if 's UNSATISFIABLE' in out else '?') + ver=None + if res=='UNSAT' and drat: + o2=subprocess.run([DT,fn,pf],capture_output=True,text=True,timeout=timeout).stdout + ver=('s VERIFIED' in o2) + if res=='SAT' and verify_model: + val=set(int(x) for l in out.splitlines() if l.startswith('v') for x in l[1:].split() if int(x)>0) + g=lambda x: 1 if (x and x in val) else 0 + idx={t:i for i,t in enumerate(D)}; Ufull={}; Vfull={} + for k,sk in enumerate(S): Ufull[sk]=[int(k==q) for q in range(r)]; Vfull[sk]=[int(k==q) for q in range(r)] + for t in rest: Ufull[t]=[g(x) for x in U[t]]; Vfull[t]=[g(x) for x in V[t]] + B={(t,s):sum(a*b for a,b in zip(Ufull[t],Vfull[s]))%2 for t in D for s in D} + ok=all(B[t,t]==1 for t in D) and all(B[t,s]==0 for t in D for s in D if t!=s and G[t][s]!=0) + rows=[sum(B[t,s]<0 else -v + Z=Lm[(Lm@v)==0] + if len(Z) and np.linalg.matrix_rank(Z.astype(float))==dim-1: out.add(tuple(int(x) for x in v)) + return sorted(out) +def allpairs(Lm,Nm,name,brute_cap=22): + G=(Lm@Lm.T).tolist(); Mk=(Nm@Lm.T)!=0; Ds={} + for a in range(len(Nm)): + for b in range(a,len(Nm)): + if Nm[a]@Nm[b]==0: continue + D=tuple(np.nonzero(Mk[a]&Mk[b])[0]); Ds.setdefault(D,(a,b)) + print(name,'lines',len(Lm),'normals',len(Nm),'distinct nonorth D',len(Ds),flush=True) + return G,Ds +mode=sys.argv[1]; FULL=len(sys.argv)>2 and sys.argv[2]=='full' +ok=True +if mode=='D4': + Lm=Dn(4); Nm=np.array(normals_of(Lm,4)); print('D4 normal supports',Counter(int((v!=0).sum()) for v in Nm)) + G,Ds=allpairs(Lm,Nm,'D4'); m=[] + for D in Ds: + x=brute_mr(G,list(D),cap=22) + if x is None: x=('>=3' if solve(G,list(D),2,seed=1)==('UNSAT',True) else 'FAIL') + m.append(x) + c=Counter(m); print('D4 mr2 over ALL nonorth pairs',c) + ok=len(Ds)==192 and set(c)<= {3,'>=3'} and c[3]+c['>=3']==192 and Counter(int((v!=0).sum()) for v in Nm)=={1:4,2:12,4:8} + print('PASS D4: 24 normals, 192 distinct nonorthogonal D-sets, every one has mr2 >= 3 (so 4 terms x 3 = 12)' if ok else 'FAIL D4') +if mode=='D6': + Lm=Dn(6); Nm=np.array(normals_of(Lm,6)); G,Ds=allpairs(Lm,Nm,'D6') + small=sorted(Ds,key=len); low=Counter(); target={10,15,16,19}; got=set() + for D in small: + D=list(D) + if len(D)>19: break + if len(D)<=8: + m=brute_mr(G,D,cap=24) + if m is not None and m<5: low[(len(D),m,'brute')]+=1 + elif FULL or (len(D) in target and len(D) not in got): + if solve(G,D,4,seed=1)==('SAT',True): low[(len(D),'<=4','SATverified')]+=1; got.add(len(D)) + print('D6 nonorth D-sets (|D|<=19 ONLY) with mr2<5:',dict(low)) + ok=low[(7,3,'brute')]>=1 and got==target and (not FULL or (low[(7,3,'brute')]==240 and len(Ds)==26096)) + print('PASS D6: counterexamples to the per-term claim (|D|=7 mr2=3; SAT-verified rank<=4 at |D|=10,15,16,19); scan limited to |D|<=19' if ok else 'FAIL D6') +if mode=='E7': + Z=np.load(sys.argv[2]); L8=Z['L']; N=Z['N']; E7=L8[(L8@L8[0])==0]; print('E7 lines',len(E7)) + N=N[(N@L8[0])==0] + keep=[k for k in range(len(N)) if np.linalg.matrix_rank(E7[(E7@N[k])==0].astype(float))==6]; N=N[keep] + G=((E7@E7.T)//4).tolist(); Mk=(N@E7.T)!=0 + n1s=np.nonzero((~Mk).sum(1)==36)[0]; seen=set(); out=[]; npairs=0 + for a in n1s: + nz=(N@N[a])!=0; sz=(Mk[a][None,:]&Mk).sum(1) + for b in np.nonzero(nz&(sz<=12))[0]: + D=tuple(np.nonzero(Mk[a]&Mk[b])[0]); npairs+=1 + if D in seen: continue + seen.add(D) + if len(out)<4: + Dl=list(D); dr=rk2([sum((1 if G[t][s]%2==0 else 0)<5 else None +T=[t for t in json.load(gzip.open(tp,'rt')) if t['nonorth']] +T.sort(key=lambda t:(len(t['D']),t['j'],t['k'])) +if sel=='sample': T=T[:14]+T[14::(len(T)-14)//18][:18] +elif sel.startswith('chunk:'): i,n=map(int,sel[6:].split('/')); T=T[i::n] +def Gof(t): + m=len(t['D']); return [[0 if (t['orth'][a]>>b&1) and a!=b else 1 for b in range(m)] for a in range(m)] +if w=='A': + import satA; run=lambda G,D,r,tag: satA.run(G,D,r,None,tag=tag,keep=keep)[:2] +else: + import satB, lib + def run(G,D,r,tag): + S=lib.nonorth_clique(G,list(reversed(D)),r) + return satB.solve(G,D,r,S) +bad=0; t0=time.time() +for t in T: + D=list(range(len(t['D']))); res=run(Gof(t),D,r,f"t{t['j']}_{t['k']}") + ok=(res[0]=='UNSAT' and res[1] is True); bad+=not ok + print('type j',t['j'],'k',t['k'],'|D|',len(D),'rank<=%d'%r,res,'OK' if ok else 'BAD',flush=True) +print('writer',w,'types',len(T),'bad',bad,'seconds',int(time.time()-t0)) +print('PASS' if bad==0 and T else 'FAIL', 'all selected types UNSAT at rank <= %d with VERIFIED DRAT (writer %s)'%(r,w)) +sys.exit(1 if bad or not T else 0) diff --git a/research/nondense-pairing-square-floors/requirements.txt b/research/nondense-pairing-square-floors/requirements.txt new file mode 100644 index 0000000000..23a04784b2 --- /dev/null +++ b/research/nondense-pairing-square-floors/requirements.txt @@ -0,0 +1,4 @@ +numpy>=2 +scipy +sympy +# tools: kissat, a C compiler (drat-trim is built from vendor/drat-trim/drat-trim.c) diff --git a/research/nondense-pairing-square-floors/vendor/drat-trim/LICENSE b/research/nondense-pairing-square-floors/vendor/drat-trim/LICENSE new file mode 100644 index 0000000000..9859ca161d --- /dev/null +++ b/research/nondense-pairing-square-floors/vendor/drat-trim/LICENSE @@ -0,0 +1,17 @@ +Copyright (c) 2014 Marijn Heule and Nathan Wetzler, The University of Texas at Austin. +Copyright (c) 2015-2016 Marijn Heule, The University of Texas at Austin. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/research/nondense-pairing-square-floors/vendor/drat-trim/drat-trim.c b/research/nondense-pairing-square-floors/vendor/drat-trim/drat-trim.c new file mode 100644 index 0000000000..cf8a676ffc --- /dev/null +++ b/research/nondense-pairing-square-floors/vendor/drat-trim/drat-trim.c @@ -0,0 +1,1501 @@ +/************************************************************************************[drat-trim.c] +Copyright (c) 2014 Marijn Heule and Nathan Wetzler, The University of Texas at Austin. +Copyright (c) 2015-2024 Marijn Heule, Carnegie Mellon University +Last edit: April 21, 2024 + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#include +#include +#include +#include + +//#define PARTIALPROOF + +#define TIMEOUT 40000 +#define BIGINIT 1000000 +#define INIT 4 +#define END 0 +#define UNSAT 0 +#define SAT 1 +#define DERIVATION 2 +#define ID -1 +#define PIVOT -2 +#define MAXDEP -3 +#define EXTRA 4 // ID + PIVOT + MAXDEP + terminating 0 +#define INFOBITS 2 // could be 1 for SAT, must be 2 for QBF +#define DBIT 1 +#define ASSUMED 2 +#define MARK 3 +#define ERROR -1 +#define ACTIVE 1 + +#define FORWARD_SAT 10 +#define FORWARD_UNSAT 20 +#define BACKWARD_UNSAT 30 +#define SUCCESS 40 +#define FAILED 50 +#define FIXPOINT 60 +#define NOWARNING 70 +#define HARDWARNING 80 + +#define COMPRESS + +struct solver { FILE *inputFile, *proofFile, *lratFile, *traceFile, *activeFile; + int *DB, nVars, timeout, mask, delete, *falseStack, *falseA, *forced, binMode, optimize, binOutput, + *processed, *assigned, count, *used, *max, COREcount, RATmode, RATcount, nActive, *lratTable, + nLemmas, maxRAT, *preRAT, maxDependencies, nDependencies, bar, backforce, reduce, + *dependencies, maxVar, maxSize, mode, verb, unitSize, unitStackSize, prep, *current, nRemoved, warning, + delProof, *setMap, *setTruth, rupOnly; + char *coreStr, *lemmaStr; + struct timeval start_time; + long mem_used, time, nClauses, nStep, nOpt, nAlloc, *unitStack, *reason, lemmas, nResolve, *RATset, + nReads, nWrites, lratSize, lratAlloc, *lratLookup, **wlist, *optproof, *formula, *proof; }; + +static inline void assign (struct solver* S, int lit) { + S->falseA[-lit] = 1; *(S->assigned++) = -lit; } + +int compare (const void *a, const void *b) { + return (*(int*)a - *(int*)b); } + +int abscompare (const void *a, const void *b) { + return (abs(*(int*)a) - abs(*(int*)b)); } + +static inline void printClause (int* clause) { + printf ("[%i] ", clause[ID]); + while (*clause) printf ("%i ", *clause++); printf ("0\n"); } + +static inline void addWatchPtr (struct solver* S, int lit, long watch) { + if (S->used[lit] + 1 == S->max[lit]) { S->max[lit] *= 1.5; + S->wlist[lit] = (long *) realloc (S->wlist[lit], sizeof (long) * S->max[lit]); + if (S->wlist[lit] == NULL) { printf("c MEMOUT: reallocation failed for watch list of %i\n", lit); exit (0); } } + S->wlist[lit][ S->used[lit]++ ] = watch | S->mask; + S->wlist[lit][ S->used[lit] ] = END; } + +static inline void addWatch (struct solver* S, int* clause, int index) { + addWatchPtr (S, clause[index], ((long) (((clause) - S->DB)) << 1)); } + +static inline void removeWatch (struct solver* S, int* clause, int index) { + int i, lit = clause[index]; + if ((S->used[lit] > INIT) && (S->max[lit] > 2 * S->used[lit])) { + S->max[lit] = (3 * S->used[lit]) >> 1; + S->wlist[lit] = (long *) realloc (S->wlist[lit], sizeof (long) * S->max[lit]); + assert(S->wlist[lit] != NULL); } + long *watch = S->wlist[lit]; + for (i = 0; i < S->used[lit]; i++) { + int* _clause = S->DB + (*(watch++) >> 1); + if (_clause == clause) { + watch[-1] = S->wlist[lit][ --S->used[lit] ]; + S->wlist[lit][ S->used[lit] ] = END; return; } } } + +static inline void addUnit (struct solver* S, long index) { +// printf("c adding unit %i\n", S->DB[index]); + if (S->unitSize >= S->unitStackSize) { + S->unitStackSize = (S->unitStackSize * 3) >> 1; + S->unitStack = (long*) realloc (S->unitStack, sizeof(long) * S->unitStackSize); + if (S->unitStack == NULL) { + printf ("c failed to reallocate unit stack\n"); exit (0); } } + S->unitStack[S->unitSize++] = index; } + +static inline void removeUnit (struct solver* S, int lit) { + int i, found = 0; + for (i = 0; i < S->unitSize; i++) { + if (found) S->unitStack[i - 1] = S->unitStack[i]; + if (S->DB[ S->unitStack[i] ] == lit) found = 1; } + S->unitSize--; } + +static inline void unassignUnit (struct solver* S, int lit) { + if (S->verb) + printf ("\rc removing unit %i\n", lit); + while (S->falseA[-lit]) { + if (S->verb) + printf ("\rc removing unit %i (%i)\n", S->forced[-1], lit); + S->falseA[*(--S->forced)] = 0; + S->reason[abs(*S->forced)] = 0; } + S->processed = S->assigned = S->forced; } + +static inline void markWatch (struct solver* S, int* clause, int index, int offset) { + long* watch = S->wlist[ clause[ index ] ]; + for (;;) { + int *_clause = (S->DB + (*(watch++) >> 1) + (long) offset); + if (_clause == clause) { watch[ID] |= ACTIVE; return; } } } + +static inline void addDependency (struct solver* S, int dep, int forced) { + if (1 || S->traceFile || S->lratFile) { // temporary for MAXDEP + if (S->nDependencies == S->maxDependencies) { + S->maxDependencies = (S->maxDependencies * 3) >> 1; +// printf ("c dependencies increased to %i\n", S->maxDependencies); + S->dependencies = realloc (S->dependencies, sizeof (int) * S->maxDependencies); + if (S->dependencies == NULL) { printf ("c MEMOUT: dependencies reallocation failed\n"); exit (0); } } +// printf("c adding dep %i\n", (dep << 1) + forced); + S->dependencies[S->nDependencies++] = (dep << 1) + forced; } } + +static inline void markClause (struct solver* S, int* clause, int index) { + S->nResolve++; + addDependency (S, clause[index - 1] >> 1, (S->assigned > S->forced)); + + if ((clause[index + ID] & ACTIVE) == 0) { + S->nActive++; + clause[index + ID] |= ACTIVE; +//#ifdef PARTIALPROOF +// if ((clause + index) > (S->DB + S->firstLemma)) // don't delete original clauses +//#endif + if ((S->mode == BACKWARD_UNSAT) && clause[index + 1]) { + S->optproof[S->nOpt++] = (((long) (clause - S->DB) + index) << INFOBITS) + 1; } + if (clause[1 + index] == 0) return; + markWatch (S, clause, index, -index); + markWatch (S, clause, 1 + index, -index); } + while (*clause) S->falseA[*(clause++)] = MARK; } + +void analyze (struct solver* S, int* clause, int index) { // Mark all clauses involved in conflict + markClause (S, clause, index); + while (S->assigned > S->falseStack) { + int lit = *(--S->assigned); + if (S->falseA[lit] == MARK) { + if (S->reason[abs (lit)]) { + markClause (S, S->DB + S->reason[abs (lit)], -1); + if (S->assigned >= S->forced) + S->reason[abs (lit)] = 0; } } +#ifndef PARTIALPROOF + else if (S->falseA[lit] == ASSUMED && !S->RATmode && S->reduce && !S->lratFile) { // Remove unused literal + S->nRemoved++; + int *tmp = S->current; + while (*tmp != lit) tmp++; + while (*tmp) { tmp[0] = tmp[1]; tmp++; } + tmp[-1] = 0; } +#endif + if (S->assigned >= S->forced) S->reason[abs (lit)] = 0; + S->falseA[lit] = (S->assigned < S->forced); } + + S->processed = S->assigned = S->forced; } + +void noAnalyze (struct solver* S) { + while (S->assigned > S->falseStack) { + int lit = *(--S->assigned); + if (S->assigned >= S->forced) S->reason[abs (lit)] = 0; + S->falseA[lit] = (S->assigned < S->forced); } + + S->processed = S->assigned = S->forced; } + +int propagate (struct solver* S, int init, int mark) { // Performs unit propagation (init not used?) + int *start[2]; + int check = 0, mode = !S->prep; + int i, lit, _lit = 0; long *watch, *_watch; + start[0] = start[1] = S->processed; + flip_check:; + check ^= 1; + while (start[check] < S->assigned) { // While unprocessed false literals + lit = *(start[check]++); // Get first unprocessed literal + if (lit == _lit) watch = _watch; + else watch = S->wlist[ lit ]; // Obtain the first watch pointer + while (*watch != END) { // While there are watched clauses (watched by lit) + if ((*watch & mode) != check) { + watch++; continue; } + int *clause = S->DB + (*watch >> 1); // Get the clause from DB + if (S->falseA[ -clause[0] ] || + S->falseA[ -clause[1] ]) { + watch++; continue; } + if (clause[0] == lit) clause[0] = clause[1]; // Ensure that the other watched literal is in front + for (i = 2; clause[i]; ++i) // Scan the non-watched literals + if (S->falseA[ clause[i] ] == 0) { // When clause[j] is not false, it is either true or unset + clause[1] = clause[i]; clause[i] = lit; // Swap literals + addWatchPtr (S, clause[1], *watch); // Add the watch to the list of clause[1] + *watch = S->wlist[lit][ --S->used[lit] ]; // Remove pointer + S->wlist[lit][ S->used[lit] ] = END; + goto next_clause; } // Goto the next watched clause + clause[1] = lit; watch++; // Set lit at clause[1] and set next watch + if (!S->falseA[ clause[0] ]) { // If the other watched literal is falsified, + assign (S, clause[0]); // A unit clause is found, and the reason is set + S->reason[abs (clause[0])] = ((long) ((clause)-S->DB)) + 1; + if (!check) { + start[0]--; _lit = lit; _watch = watch; + goto flip_check; } } + else if (!mark) { noAnalyze (S); return UNSAT; } + else { analyze (S, clause, 0); return UNSAT; } // Found a root level conflict -> UNSAT + next_clause: ; } } // Set position for next clause + if (check) goto flip_check; + S->processed = S->assigned; + return SAT; } // Finally, no conflict was found + + +// Propagate top level units +static inline int propagateUnits (struct solver* S, int init) { + int i; +// printf("c propagateUnits %i\n", S->unitSize); + while (S->forced > S->falseStack) { + S->falseA[*(--S->forced)] = 0; + S->reason[abs (*S->forced)] = 0; } + S->forced = S->assigned = S->processed = S->falseStack; + for (i = 0; i < S->unitSize; i++) { + int lit = S->DB[ S->unitStack[i] ]; + S->reason[abs (lit)] = S->unitStack[i] + 1; + assign (S, lit); } + + if (propagate (S, init, 1) == UNSAT) { return UNSAT; } + S->forced = S->processed; + return SAT; } + +// Put falsified literals at the end and returns the size under the current +// assignment: negative size means satisfied, size = 0 means falsified +int sortSize (struct solver *S, int *lemma) { + unsigned int size = 0, last = 0, sat = 1; + while (lemma[last]) { + int lit = lemma[last++]; + if (S->falseA[lit] == 0) { + if (S->falseA[-lit]) sat = -1; + lemma[last-1] = lemma[ size ]; + lemma[size++] = lit; } } + return sat * size; } + +// print the core clauses to coreFile in DIMACS format +void printCore (struct solver *S) { + int i, j; + for (i = 0; i < S->nClauses; i++) { + int *clause = S->DB + (S->formula[i] >> INFOBITS); + if (clause[ID] & ACTIVE) S->COREcount++; } + printf ("\rc %i of %li clauses in core \n", S->COREcount, S->nClauses); + + if (S->coreStr) { + FILE *coreFile = fopen (S->coreStr, "w"); + fprintf (coreFile, "p cnf %i %i\n", S->nVars, S->COREcount); + for (i = 0; i < S->nClauses; i++) { + int *clause = S->DB + (S->formula[i] >> INFOBITS); + if (clause[ID] & ACTIVE) { + while (*clause) fprintf (coreFile, "%i ", *clause++); + fprintf (coreFile, "0\n"); } } + fclose (coreFile); } } + +void write_lit (struct solver *S, FILE *output, int lit) { // change to long? + unsigned int l = abs (lit) << 1; + if (lit < 0) l++; + + do { + if (l <= 127) { fputc ((char) l, output); } + else { fputc ((char) (128 + (l & 127)), output); } + S->nWrites++; + l = l >> 7; } + while (l); } + +void printLRATline (struct solver *S, int time) { + int *line = S->lratTable + S->lratLookup[time]; + if (S->binOutput) { + fputc ('a', S->lratFile); S->nWrites++; + while (*line) write_lit (S, S->lratFile, *line++); + write_lit (S, S->lratFile, *line++); + while (*line) write_lit (S, S->lratFile, *line++); + write_lit (S, S->lratFile, *line++); } + else { + while (*line) fprintf (S->lratFile, "%i ", *line++); + fprintf (S->lratFile, "%i ", *line++); + while (*line) fprintf (S->lratFile, "%i ", *line++); + fprintf (S->lratFile, "%i\n", *line++); } } + +// print the core lemmas to lemmaFile in DRAT format +void printProof (struct solver *S) { + int step; + printf ("\rc %i of %i lemmas in core using %lu resolution steps\n", S->nActive - S->COREcount + 1, S->nLemmas + 1, S->nResolve); + printf ("\rc %d RAT lemmas in core; %i redundant literals in core lemmas\n", S->RATcount, S->nRemoved); + + // NB: not yet working with forward checking + if (S->mode == FORWARD_UNSAT) { + printf ("\rc optimized proofs are not supported for forward checking\n"); + return; } + + // replace S->proof by S->optproof + if (S->mode == BACKWARD_UNSAT) { + if (S->nOpt > S->nAlloc) { + S->nAlloc = S->nOpt; + S->proof = (long*) realloc (S->proof, sizeof (long) * S->nAlloc); + if (S->proof == NULL) { printf("c MEMOUT: reallocation of proof list failed\n"); exit (0); } } + S->nStep = 0; + S->nLemmas = 0; + for (step = S->nOpt - 1; step >= 0; step--) { + long ad = S->optproof[step]; + int *lemmas = S->DB + (ad >> INFOBITS); + if ((ad & 1) == 0) S->nLemmas++; +// if (lemmas[ID] & ACTIVE) lemmas[ID] ^= ACTIVE; // only useful for checking multiple times? + S->proof[S->nStep++] = S->optproof[step]; } } // why not reuse ad? + + if (S->lemmaStr) { + FILE *lemmaFile = fopen (S->lemmaStr, "w"); + for (step = 0; step < S->nStep; step++) { + long ad = S->proof[step]; + int *lemmas = S->DB + (ad >> INFOBITS); + if (!lemmas[1] && (ad & 1)) continue; // don't delete unit clauses + if (S->binOutput) { + S->nWrites++; + if (ad & 1) fputc ('d', lemmaFile); + else fputc ('a', lemmaFile); } + else if (ad & 1) fprintf (lemmaFile, "d "); + int reslit = lemmas[PIVOT]; + while (*lemmas) { + int lit = *lemmas++; + if (lit == reslit) { + if (S->binOutput) write_lit (S, lemmaFile, lit); + else fprintf (lemmaFile, "%i ", lit); } } + lemmas = S->DB + (ad >> INFOBITS); + while (*lemmas) { + int lit = *lemmas++; + if (lit != reslit) { + if (S->binOutput) write_lit (S, lemmaFile, lit); + else fprintf (lemmaFile, "%i ", lit); } } + if (S->binOutput) write_lit (S, lemmaFile, 0); + else fprintf (lemmaFile, "0\n"); } + if (S->binOutput) { fputc ('a', lemmaFile); write_lit (S, lemmaFile, 0); } + else fprintf (lemmaFile, "0\n"); + fclose (lemmaFile); } + + if (S->lratFile) { + int lastAdded = S->nClauses; + int flag = 0; + for (step = 0; step < S->nStep; step++) { + long ad = S->proof[step]; + int *lemmas = S->DB + (ad >> INFOBITS); + if ((ad & 1) == 0) { + if (lastAdded == 0) { + if (S->binOutput) { + write_lit (S, S->lratFile, 0); } + else { + fprintf (S->lratFile, "0\n"); } } + lastAdded = lemmas[ID] >> 1; + printLRATline (S, lastAdded); } + else if (lastAdded == S->nClauses) continue; + else if (!lemmas[1] && (ad & 1)) continue; // don't delete unit clauses + else if (ad & 1) { + if (lastAdded != 0) { + if (S->binOutput) { + fputc ('d', S->lratFile); S->nWrites++; } + else { + fprintf (S->lratFile, "%i d ", lastAdded); } } + lastAdded = 0; + if (S->binOutput) { + write_lit (S, S->lratFile, lemmas[ID] >> 1); } + else { + fprintf (S->lratFile, "%i ", lemmas[ID] >> 1); } } } + if (lastAdded != S->nClauses) { + if (S->binOutput) { + write_lit (S, S->lratFile, 0); } + else { + fprintf(S->lratFile, "0\n"); } } + + printLRATline (S, S->count); + + fclose (S->lratFile); + if (S->nWrites) + printf ("c wrote optimized proof in LRAT format of %li bytes\n", S->nWrites); } } + +void printNoCore (struct solver *S) { + if (S->lratFile) { + if (S->binOutput) { + fputc ('d', S->lratFile); S->nWrites++; } + else { + fprintf (S->lratFile, "%ld d ", S->nClauses); } + int i; + for (i = 0; i < S->nClauses; i++) { + int *clause = S->DB + (S->formula[i] >> INFOBITS); + if ((clause[ID] & ACTIVE) == 0) { + if (S->binOutput) { + write_lit (S, S->lratFile, clause[ID] >> 1); } + else { + fprintf (S->lratFile, "%i ", clause[ID] >> 1); } } } + if (S->binOutput) { + write_lit (S, S->lratFile, 0); } + else { + fprintf (S->lratFile, "0\n"); } } } + +// print the dependency graph to traceFile in TraceCheck+ format +// this procedure adds the active clauses at the end of the trace +void printTrace (struct solver *S) { + if (S->traceFile) { int i; + for (i = 0; i < S->nClauses; i++) { + int *clause = S->DB + (S->formula[i] >> INFOBITS); + if (clause[ID] & ACTIVE) { + fprintf (S->traceFile, "%i ", i + 1); + while (*clause) fprintf (S->traceFile, "%i ", *clause++); + fprintf (S->traceFile, "0 0\n"); } } + fclose (S->traceFile); } } + +void printActive (struct solver *S) { + int i, j; + if (S->activeFile) { + for (i = -S->maxVar; i <= S->maxVar; i++) + if (i != 0) + for (j = 0; j < S->used[i]; j++) { + int *clause = S->DB + (S->wlist[i][j] >> 1); + if (*clause == i) { + while (*clause) + fprintf (S->activeFile, "%i ", *clause++); + fprintf (S->activeFile, "0\n"); } } } } + +void postprocess (struct solver *S) { + printNoCore (S); // print before proof optimization + printActive (S); + printCore (S); + printTrace (S); // closes traceFile + printProof (S); } // closes lratFile + +void lratAdd (struct solver *S, int elem) { + if (S->lratSize == S->lratAlloc) { + S->lratAlloc = S->lratAlloc * 3 >> 1; + S->lratTable = (int *) realloc (S->lratTable, sizeof (int) * S->lratAlloc); + if (S->lratTable == NULL) { + printf ("c MEMOUT: failed to reallocate lrat table\n"); exit (0); } } + S->lratTable[S->lratSize++] = elem; } + +void printDependenciesFile (struct solver *S, int* clause, int RATflag, int mode) { + FILE *file = NULL; + if (mode == 0) file = S->traceFile; + if (mode == 1) file = S->lratFile; + + if (file) { + int i, j, k; + int tmp = S->lratSize; + + if (clause != NULL) { + S->lratLookup[clause[ID] >> 1] = S->lratSize; } + else { S->lratLookup[S->count] = S->lratSize; } + + if (clause != NULL) { + int size = 0; + int *sortClause; + + sortClause = (int *) malloc (sizeof(int) * S->maxSize); + assert (sortClause != NULL); + lratAdd (S, S->time >> 1); // NB: long to ing + int reslit = clause[PIVOT]; + while (*clause) { + if (*clause == reslit) + lratAdd (S, reslit); + sortClause[size++] = *clause++; } + qsort (sortClause, size, sizeof (int), abscompare); + for (i = 0; i < size; i++) { + int lit = sortClause[i]; + if (lit != reslit) + lratAdd (S, lit); } + free (sortClause); } + else { lratAdd (S, S->count); } + lratAdd (S, 0); + + int isRUP = 1; + for (i = 0; i < S->nDependencies; i++) + if (S->dependencies[i] < 0) { isRUP = 0; break; } + + if (isRUP) { + for (i = S->nDependencies - 1; i >= 0; i--) + lratAdd (S, S->dependencies[i] >> 1); + lratAdd (S, 0); + goto printLine; } + + // first print the preRAT units in order of becoming unit + int size = 0; + for (i = 0; i < S->nDependencies; i++) { + if (S->dependencies[i] > 0) continue; + for (j = i - 1; j >= 0 && S->dependencies[j] > 0; j--) { + int flag = 0; + int cls = S->dependencies[j]; + if (cls & 1) continue; + for (k = 0; k < size; k++) + if (S->preRAT[k] == cls) flag = 1; + if (!flag) { + S->preRAT[size++] = cls; + lratAdd (S, cls >> 1); } } } + + // print dependencies in order of becoming unit + for (i = S->nDependencies - 1; i >= 0; i--) { + int cls = S->dependencies[i]; + if ((mode == 0) && (cls < 0)) continue; + if (mode == 0) { + int flag = 0; + for (j = 0; j < size; j++) + if (S->preRAT[j] == cls) flag = 1; + if (!flag) { + S->preRAT[size++] = cls; + lratAdd (S, cls >> 1); } } + if ((mode == 1) && (cls & 1)) + lratAdd (S, cls >> 1); } + lratAdd (S, 0); + + printLine:; + if (mode == 0) { + for (i = tmp; i < S->lratSize; i++) + fprintf (file, "%d ", S->lratTable[i]); + S->lratSize = tmp; + fprintf (file, "\n"); } } } + +void printDependencies (struct solver *S, int* clause, int RATflag) { + if (clause != NULL) { + int i; + clause[MAXDEP] = 0; + for (i = 0; i < S->nDependencies; i++) { + if (S->dependencies[i] > clause[MAXDEP]) + clause[MAXDEP] = S->dependencies[i]; } + assert (clause[MAXDEP] < clause[ID]); } + + printDependenciesFile (S, clause, RATflag, 0); + printDependenciesFile (S, clause, RATflag, 1); } + +int checkRAT (struct solver *S, int pivot, int mark) { + int i, j, nRAT = 0; + + // Loop over all literals to calculate resolution candidates + for (i = -S->maxVar; i <= S->maxVar; i++) { + if (i == 0) continue; + // Loop over all watched clauses for literal + for (j = 0; j < S->used[i]; j++) { + int* watched = S->DB + (S->wlist[i][j] >> 1); + int id = watched[ID] >> 1; + int active = watched[ID] & ACTIVE; + if (*watched == i) { // If watched literal is in first position + while (*watched) + if (*watched++ == -pivot) { + if ((S->mode == BACKWARD_UNSAT) && !active) { +// printf ("\rc RAT check ignores unmarked clause : "); printClause (S->DB + (S->wlist[i][j] >> 1)); + continue; } + if (nRAT == S->maxRAT) { + S->maxRAT = (S->maxRAT * 3) >> 1; + S->RATset = realloc (S->RATset, sizeof (long) * S->maxRAT); + assert (S->RATset != NULL); } + S->RATset[nRAT++] = S->wlist[i][j] >> 1; + break; } } } } + + // S->prep = 1; + // Check all clauses in RATset for RUP + int flag = 1; + qsort (S->RATset, nRAT, sizeof (long), compare); + S->nDependencies = 0; + for (i = nRAT - 1; i >= 0; i--) { + int* RATcls = (int*) (S->DB + S->RATset[i]); + int id = RATcls[ID] >> 1; + int blocked = 0; + long int reason = 0; + if (S->verb) { + printf ("\rc RAT clause: "); printClause (RATcls); } + + while (*RATcls) { + int lit = *RATcls++; + if (lit != -pivot && S->falseA[-lit]) + if (!blocked || reason > S->reason[abs (lit)]) + blocked = lit, reason = S->reason[abs (lit)]; } + + if (blocked && reason) { + analyze (S, S->DB + reason, -1); + S->reason[abs (blocked)] = 0; } + + if (!blocked) { + RATcls = (int*) (S->DB + S->RATset[i]); + while (*RATcls) { + int lit = *RATcls++; + if (lit != -pivot && !S->falseA[lit]) { + assign (S, -lit); S->reason[abs (lit)] = 0; } } + if (propagate (S, 0, mark) == SAT) { flag = 0; break; } } + addDependency (S, -id, 1); } + + if (flag == 0) { + while (S->forced < S->assigned) { + S->falseA[*(--S->assigned)] = 0; + S->reason[abs (*S->assigned)] = 0; } + if (S->verb) printf ("\rc RAT check on pivot %i failed\n", pivot); + return FAILED; } + + return SUCCESS; } + +int redundancyCheck (struct solver *S, int *clause, int size, int mark) { + int i, indegree; + int falsePivot = S->falseA[clause[PIVOT]]; + if (S->verb) { printf ("\rc checking lemma (%i, %i) ", size, clause[PIVOT]); printClause (clause); } + + if (S->mode != FORWARD_UNSAT) { + if ((clause[ID] & ACTIVE) == 0) return SUCCESS; } // redundant? +// clause[PIVOT] ^= ACTIVE; } + + if (size < 0) { + S->DB[ S->reason[abs (*clause)] - 2] |= 1; + return SUCCESS; } + + indegree = S->nResolve; + + S->RATmode = 0; + S->nDependencies = 0; + for (i = 0; i < size; ++i) { + if (S->falseA[-clause[i]]) { // should only occur in forward mode + if (S->warning != NOWARNING) { + printf ("\rc WARNING: found a tautological clause in proof: "); printClause (clause); } + if (S->warning == HARDWARNING) exit (HARDWARNING); + while (S->forced < S->assigned) { + S->falseA[*(--S->assigned)] = 0; + S->reason[abs (*S->assigned)] = 0; } + return SUCCESS; } + S->falseA[clause[i]] = ASSUMED; + *(S->assigned++) = clause[i]; + S->reason[abs (clause[i])] = 0; } + + S->current = clause; + if (propagate (S, 0, mark) == UNSAT) { + indegree = S->nResolve - indegree; + if (indegree <= 2 && S->prep == 0) { + S->prep = 1; if (S->verb) printf ("\rc [%li] preprocessing checking mode on\n", S->time); } + if (indegree > 2 && S->prep == 1) { + S->prep = 0; if (S->verb) printf ("\rc [%li] preprocessing checking mode off\n", S->time); } + if (S->verb) printf ("\rc lemma has RUP\n"); + printDependencies (S, clause, 0); + return SUCCESS; } + + // Failed RUP check. Now test RAT. + // printf ("RUP check failed. Starting RAT check.\n"); + if (S->rupOnly) { + printf ("\rc RUP check failed\n"); + return FAILED; } + int reslit = clause[PIVOT]; + if (S->verb) + printf ("\rc RUP checked failed; starting RAT check on pivot %d.\n", reslit); + + if (falsePivot) return FAILED; + + int* savedForced = S->forced; + + S->RATmode = 1; + S->forced = S->assigned; + + int failed = 0; + if (checkRAT (S, reslit, mark) == FAILED) { + failed = 1; + if (S->warning != NOWARNING) { + printf ("\rc WARNING: RAT check on proof pivot failed : "); printClause (clause); } + if (S->warning == HARDWARNING) exit (HARDWARNING); + for (i = 0; i < size; i++) { + if (clause[i] == reslit) continue; + if (checkRAT (S, clause[i], mark) == SUCCESS) { + clause[PIVOT] = clause[i]; + failed = 0; break; } } } + + if (failed == 0) + printDependencies (S, clause, 1); + + S->processed = S->forced = savedForced; + while (S->forced < S->assigned) { + S->falseA[*(--S->assigned)] = 0; + S->reason[abs (*S->assigned)] = 0; } + + if (failed) { + printf ("c RAT check failed on all possible pivots\n"); + return FAILED; } + + + if (mark) S->RATcount++; + if (S->verb) printf ("\rc lemma has RAT on %i\n", clause[PIVOT]); + return SUCCESS; } + +int init (struct solver *S) { + S->forced = S->falseStack; // Points inside *falseStack at first decision (unforced literal) + S->processed = S->falseStack; // Points inside *falseStack at first unprocessed literal + S->assigned = S->falseStack; // Points inside *falseStack at last unprocessed literal + + // initialize watch pointers on the original clauses + S->RATmode = 0; + S->nRemoved = 0; + S->nOpt = 0; + S->nResolve = 0; + S->RATcount = 0; + S->nActive = 0; + S->COREcount = 0; + S->unitSize = 0; + + int i; + for (i = 1; i <= S->maxVar; ++i) { + S->reason [i] = 0; + S->falseStack[i] = 0; + S->falseA[i] = S->falseA[-i] = 0; + S->used [i] = S->used [-i] = 0; + S->wlist [i][0] = S->wlist [-i][0] = END; } + + for (i = 0; i < S->nClauses; i++) { + int *clause = S->DB + (S->formula[i] >> INFOBITS); + if (clause[ID] & ACTIVE) clause[ID] ^= ACTIVE; + if (clause[0] == 0) { + printf ("\rc formula contains empty clause\n"); + if (S->coreStr) { + FILE *coreFile = fopen (S->coreStr, "w"); + fprintf (coreFile, "p cnf 0 1\n 0\n"); + fclose (coreFile); } + if (S->lemmaStr) { + FILE *lemmaFile = fopen (S->lemmaStr, "w"); + fprintf (lemmaFile, "0\n"); + fclose (lemmaFile); } + return UNSAT; } + if (clause[1]) { addWatch (S, clause, 0); addWatch (S, clause, 1); } + else if (S->falseA[clause[0]]) { + printf ("\rc found complementary unit clauses: %i\n", clause[0]); + if (S->coreStr) { + FILE *coreFile = fopen (S->coreStr, "w"); + fprintf (coreFile, "p cnf %i 2\n%i 0\n%i 0\n", abs (clause[0]), clause[0], -clause[0]); + fclose (coreFile); } + if (S->lemmaStr) { + FILE *lemmaFile = fopen (S->lemmaStr, "w"); + fprintf (lemmaFile, "0\n"); + fclose (lemmaFile); } + if (S->lratFile) { + int j; + for (j = 0; j < i; j++) { + int *_clause = S->DB + (S->formula[j] >> INFOBITS); + if ((_clause[0] == -clause[0]) && !_clause[1]) break; } + fprintf (S->lratFile, "%li 0 %i %i 0\n", S->nClauses + 1, j + 1, i + 1); } + return UNSAT; } + else if (!S->falseA[ -clause[0] ]) { + addUnit (S, (long) (clause - S->DB)); + assign (S, clause[0]); } } + + S->nDependencies = 0; + S->time = S->count; // Alternative time init + if (propagateUnits (S, 1) == UNSAT) { + printf ("\rc UNSAT via unit propagation on the input instance\n"); + printDependencies (S, NULL, 0); + postprocess (S); return UNSAT; } + return SAT; } + +int verify (struct solver *S, int begin, int end) { + int top_flag = 1; + if (init (S) == UNSAT) return UNSAT; + + if (S->mode == FORWARD_UNSAT) { + if (begin == end) + printf ("\rc start forward verification\n"); } + + int step; + int adds = 0; + int active = S->nClauses; + for (step = 0; step < S->nStep; step++) { + if (step >= begin && step < end) continue; + long ad = S->proof[step]; long d = ad & 1; + int *lemmas = S->DB + (ad >> INFOBITS); + + S->time = lemmas[ID]; + if (d) { active--; } + else { active++; adds++; } + if (S->mode == FORWARD_SAT && S->verb) printf ("\rc %i active clauses\n", active); + + if (!lemmas[1]) { // found a unit + int lit = lemmas[0]; + if (S->verb) + printf ("\rc found unit in proof %i [%li]\n", lit, S->time); + if (d) { + if (S->mode == FORWARD_SAT) { + removeUnit (S, lit); propagateUnits (S, 0); } + else { // no need to remove units while checking UNSAT + if (S->verb) { printf("c removing proof step: d "); printClause(lemmas); } + S->proof[step] = 0; continue; } } + else { +// printf ("c unit %i\n", lit); +// if (S->falseA[-lit]) S->reason[abs(lit)] = (lemmas - S->DB) + 1; + if (S->mode == BACKWARD_UNSAT && S->falseA[-lit]) { S->proof[step] = 0; continue; } + else { addUnit (S, (long) (lemmas - S->DB)); } } } + + if (d && lemmas[1]) { // if delete and not unit + if ((S->reason[abs (lemmas[0])] - 1) == (lemmas - S->DB)) { // what is this check? + if (S->mode != FORWARD_SAT) { // ignore pseudo unit clause deletion + if (S->warning != NOWARNING) { + if (S->verb) { printf ("\rc WARNING: ignoring deletion instruction %li: ", (lemmas - S->DB)); printClause (lemmas); } } + if (S->warning == HARDWARNING) exit (HARDWARNING); + if (S->verb) { printf ("c ignoring deletion instruction %li: ", (lemmas - S->DB)); printClause (lemmas); } +// if (S->mode == BACKWARD_UNSAT) { // ignore pseudo unit clause deletion + S->proof[step] = 0; } + else { // if (S->mode == FORWARD_SAT) { // also for FORWARD_UNSAT? + removeWatch (S, lemmas, 0), removeWatch (S, lemmas, 1); + propagateUnits (S, 0); } } + else { + removeWatch (S, lemmas, 0), removeWatch (S, lemmas, 1); } + if (S->mode == FORWARD_UNSAT ) continue; // Ignore deletion of top-level units + if (S->mode == BACKWARD_UNSAT) continue; } + + int size = sortSize (S, lemmas); // after removal of watches + + if (d && S->mode == FORWARD_SAT) { + if (size == -1) propagateUnits (S, 0); // necessary? + if (redundancyCheck (S, lemmas, size, 1) == FAILED) { + printf ("c failed at proof line %i (modulo deletion errors)\n", step + 1); + return SAT; } + continue; } + + if (d == 0 && S->mode == FORWARD_UNSAT) { + if (step > end) { + if (size < 0) continue; // Fix of bus error: 10 + if (redundancyCheck (S, lemmas, size, 1) == FAILED) { + printf ("c failed at proof line %i (modulo deletion errors)\n", step + 1); + return SAT; } + + size = sortSize (S, lemmas); + S->nDependencies = 0; } } + + if (lemmas[1]) + addWatch (S, lemmas, 0), addWatch (S, lemmas, 1); + + if (size == 0) { printf ("\rc conflict claimed, but not detected\n"); return SAT; } // change to FAILED? + if (size == 1) { + if (S->verb) printf ("\rc found unit %i\n", lemmas[0]); + assign (S, lemmas[0]); S->reason[abs (lemmas[0])] = ((long) ((lemmas)-S->DB)) + 1; + if (propagate (S, 1, 1) == UNSAT) goto start_verification; + S->forced = S->processed; } } + + if (S->mode == FORWARD_SAT && active == 0) { + postprocess (S); return UNSAT; } + + if (S->mode == FORWARD_UNSAT) { + if (begin == end) { + postprocess (S); + printf ("\rc VERIFIED derivation: all lemmas preserve satisfiability\n"); + if (S->warning != NOWARNING) { + printf ("\rc WARNING: no empty clause detected, this is not a refutation\n"); } + return DERIVATION; } + return SAT; } + + if (S->mode == BACKWARD_UNSAT) { + if (S->backforce) { + int s; + for (s = 0; s < step; s++) { + long ad = S->proof[s]; + int *clause = S->DB + (ad >> INFOBITS); + if (sortSize(S, clause) >= 0) { + if ( (ad & 1) && (clause[ID] & ACTIVE)) clause[ID] ^= ACTIVE; + if (!(ad & 1)) clause[ID] |= ACTIVE; } } } + if (!S->backforce) { + printf ("\rc ERROR: no conflict\n"); + return SAT; + } } + + start_verification:; + if (S->mode == FORWARD_UNSAT) { + printDependencies (S, NULL, 0); + postprocess (S); return UNSAT; } + + if (!S->backforce) + printDependencies (S, NULL, 0); + + if (S->mode == FORWARD_SAT) { + printf ("\rc ERROR: found empty clause during SAT check\n"); exit (0); } + printf ("\rc detected empty clause; start verification via backward checking\n"); + + S->forced = S->processed; + assert (S->mode == BACKWARD_UNSAT); // only reachable in BACKWARD_UNSAT mode + + S->nOpt = 0; + + int checked = 0, skipped = 0; + + double max = (double) adds; + + struct timeval backward_time; + gettimeofday (&backward_time, NULL); + for (; step >= 0; step--) { + struct timeval current_time; + gettimeofday (¤t_time, NULL); + int seconds = (int) (current_time.tv_sec - S->start_time.tv_sec); + if ((seconds > S->timeout) && (S->optimize == 0)) printf ("s TIMEOUT\n"), exit (0); + + if (S->bar) + if ((adds % 1000) == 0) { + int f; + long runtime = (current_time.tv_sec - backward_time.tv_sec ) * 1000000 + + (current_time.tv_usec - backward_time.tv_usec); + double time = (double) (runtime / 1000000.0); + double fraction = (adds * 1.0) / max; + printf("\rc %.2f%% [", 100.0 * (1.0 - fraction)); + for (f = 1; f <= 20; f++) { + if ((1.0 - fraction) * 20.0 < 1.0 * f) printf(" "); + else printf("="); } + printf("] time remaining: %.2f seconds ", time / (1.0 - fraction) - time); + if (step == 0) printf("\n"); + fflush (stdout); } + + long ad = S->proof[step]; long d = ad & 1; + int *clause = S->DB + (ad >> INFOBITS); + + + if (ad == 0) continue; // Skip lemma that has been removed from proof + if ( d == 0) { + adds--; + if (clause[1]) { + removeWatch (S, clause, 0), removeWatch (S, clause, 1); + if (S->reason[abs (clause[0])] == (clause + 1 - S->DB)) { // use this check also for units? + unassignUnit (S, clause[0]); } } + else unassignUnit (S, clause[0]); } + + int size = sortSize (S, clause); + + if (d) { + if (S->verb) { printf ("\rc adding clause (%i) ", size); printClause (clause); } + addWatch (S, clause, 0), addWatch (S, clause, 1); continue; } + + S->time = clause[ID]; + if ((S->time & ACTIVE) == 0) { + skipped++; +// if ((skipped % 100) == 0) printf("c skipped %i, checked %i\n", skipped, checked); + continue; } // If not marked, continue + + assert (size >= 1); + int *_clause = clause + size; + while (*_clause++) { S->nRemoved++; } + clause[size] = 0; + + if (S->verb) { + printf ("\rc validating clause (%i, %i): ", clause[PIVOT], size); printClause (clause); } + + if (redundancyCheck (S, clause, size, 1) == FAILED) { + printf ("c failed at proof line %i (modulo deletion errors)\n", step + 1); + return SAT; } + checked++; + S->optproof[S->nOpt++] = ad; } + + postprocess (S); + return UNSAT; } + +long matchClause (struct solver* S, long *clauselist, int listsize, int* input, int size) { + int i, j; + for (i = 0; i < listsize; ++i) { + int *clause = S->DB + clauselist[i]; + for (j = 0; j <= size; j++) + if (clause[j] != input[j]) goto match_next; + + long result = clauselist[i]; + clauselist[i] = clauselist[--listsize]; + return result; + match_next:; } + return 0; } + +unsigned int getHash (int* input) { + unsigned int sum = 0, prod = 1, xor = 0; + while (*input) { + prod *= *input; sum += *input; xor ^= *input; input++; } + return (1023 * sum + prod ^ (31 * xor)) % BIGINIT; } + +int read_lit (struct solver *S, int *lit) { + int l = 0, lc, shift = 0; + do { + lc = getc_unlocked (S->proofFile); + S->nReads++; + if ((shift == 0) && (lc == EOF)) return EOF; + l |= (lc & 127) << shift; + shift += 7; } + while (lc > 127); + if (l % 2) *lit = (l >> 1) * -1; + else *lit = (l >> 1); + return 1; } + +void deactivate (struct solver *S) { + S->nActive = 0; + int step; + for (step = 0; step < S->nStep; step++) { + if ((S->proof[step] & 1) == 0) { + int *clause = S->DB + (S->proof[step] >> INFOBITS); + if (clause[ID] & ACTIVE) clause[ID] ^= ACTIVE; } } +} + +void shuffleProof (struct solver *S, int iteration) { + int i, step, _step; + + double base = 100; + for (i = 1; i < iteration; i++) + base *= 1.1; + + // randomly remove clause deletion steps + for (_step = 0, step = 0; step < S->nStep; step++) { + if (S->proof[step] & 1) { + int length = 0; + int *clause = S->DB + (S->proof[step] >> INFOBITS); + while (*clause) { length++; clause++; } + if ((rand() % 1000) < (base * iteration / length)) continue; } + S->proof[_step++] = S->proof[step]; } + S->nStep = _step; + + for (step = S->nStep - 1; step > 0; step--) { + long a = S->proof[step ]; + if (a & DBIT) continue; + long b = S->proof[step-1]; + if (b & DBIT) { + S->proof[step ] = b; + S->proof[step-1] = a; } + else { + int *c = S->DB + (a >> INFOBITS); + int *d = S->DB + (b >> INFOBITS); + int coinflip = 0; +// int coinflip = rand () / (RAND_MAX >> 1); + if (c[MAXDEP] < d[MAXDEP] || (coinflip && (c[MAXDEP] < d[ID]))) { + int tmp = d[ID]; + d[ID] = c[ID]; + c[ID] = tmp; + S->proof[step ] = b; + S->proof[step-1] = a; } } } + + for (step = 0; step < S->nStep; step++) { + long ad = S->proof[step]; + if (ad & 1) continue; + int *clause = S->DB + (ad >> INFOBITS); + int i, length = 0; + while (*clause) { length++; clause++; } + clause = S->DB + (ad >> INFOBITS); + for (i = 0; i < length - 1; i++) { + int j = i + rand() / (RAND_MAX / (length - i) + 1); + int t = clause[i]; clause[i] = clause[j]; clause[j] = t; } } } + +int parse (struct solver* S) { + int tmp, active = 0, retvalue = SAT; + int del = 0, fileLine = 0; + int *buffer, bufferAlloc; + + S->nVars = 0; + S->nClauses = 0; + do { tmp = fscanf (S->inputFile, " cnf %i %li \n", &S->nVars, &S->nClauses); // Read the first line + if (tmp > 0 && tmp != EOF) break; tmp = fscanf (S->inputFile, "%*s\n"); } // In case a commment line was found + while (tmp != 2 && tmp != EOF); // Skip it and read next line + int nZeros = S->nClauses; + + if (!S->nVars && !S->nClauses) { + printf ("\rc ERROR: did not find p cnf line in input file\n"); exit (0); } + + printf ("\rc parsing input formula with %i variables and %li clauses\n", S->nVars, S->nClauses); + + bufferAlloc = INIT; + buffer = (int*) malloc (sizeof (int) * bufferAlloc); + + S->count = 1; + S->nStep = 0; + S->mem_used = 0; // The number of integers allocated in the DB + + long size; + long DBsize = S->mem_used + BIGINIT; + S->DB = (int*) malloc (DBsize * sizeof (int)); + if (S->DB == NULL) { free (buffer); return ERROR; } + + S->maxVar = 0; + S->maxSize = 0; + S->nLemmas = 0; + S->nAlloc = BIGINIT; + S->formula = (long *) malloc (sizeof (long) * S->nClauses); + S->proof = (long *) malloc (sizeof (long) * S->nAlloc); + long **hashTable = (long**) malloc (sizeof (long*) * BIGINIT); + int *hashUsed = (int * ) malloc (sizeof (int ) * BIGINIT); + int *hashMax = (int * ) malloc (sizeof (int ) * BIGINIT); + + int i; + for (i = 0; i < BIGINIT; i++) { + hashUsed [i] = 0; + hashMax [i] = INIT; + hashTable[i] = (long*) malloc (sizeof (long) * hashMax[i]); } + + int fileSwitchFlag = 0; + long finalClause = 0; + size = 0; + while (1) { + int lit = 0; tmp = 0; + fileSwitchFlag = nZeros <= 0; + + if (size == 0) { + if (fileSwitchFlag) { // read for proof + if (S->binMode == 1) { + int res = getc_unlocked (S->proofFile); + if (res == EOF) break; + else if (res == 97) del = 0; + else if (res == 100) del = 1; + else { + if (S->warning != NOWARNING) { + printf ("\rc WARNING: wrong binary prefix, stop reading proof\n"); break; } + if (S->warning == HARDWARNING) exit (HARDWARNING); } + S->nReads++; } + else { + tmp = fscanf (S->proofFile, " d %i ", &lit); + if (tmp == EOF) break; + del = tmp > 0; } } } + + if (!lit) { + if (!fileSwitchFlag) tmp = fscanf (S->inputFile, " %i ", &lit); // Read a literal. + else { + if (S->binMode == 1) { + tmp = read_lit (S, &lit); } + else { + tmp = fscanf (S->proofFile, " %i ", &lit); } } + if (tmp == EOF && !fileSwitchFlag) { + if (S->warning != NOWARNING) { + printf ("\rc WARNING: early EOF of the input formula\n"); + printf ("\rc WARNING: %i clauses less than expected\n", nZeros); } + if (S->warning == HARDWARNING) exit (HARDWARNING); + fileLine = 0; + fileSwitchFlag = 1; } } + + if (!tmp && !S->binMode) { + char ignore[1<<16]; + if (!fileSwitchFlag) { if (fgets (ignore, sizeof (ignore), S->inputFile) == NULL) printf ("c\n"); } + else if (fgets (ignore, sizeof (ignore), S->proofFile) == NULL) printf ("c\n"); + if (ignore[0] != 'c') continue; + for (i = 0; i < sizeof ignore; i++) { if (ignore[i] == '\n') break; } + if (i == sizeof ignore) { + printf ("c ERROR: comment longer than %zu characters: %s\n", sizeof ignore, ignore); + exit (HARDWARNING); } + if (S->verb) printf ("\rc WARNING: parsing mismatch assuming a comment\n"); + continue; } + + if (abs (lit) > S->maxVar) S->maxVar = abs (lit); + if (tmp == EOF && fileSwitchFlag) break; + if (abs (lit) > S->nVars && !fileSwitchFlag) { + printf ("\rc illegal literal %i due to max var %i\n", lit, S->nVars); exit (0); } + if (!lit) { + fileLine++; + if (size > S->maxSize) S->maxSize = size; + int pivot = buffer[0]; + buffer[size] = 0; + qsort (buffer, size, sizeof (int), compare); + int j = 0; + for (i = 0; i < size; ++i) { + if (buffer[i] == buffer[i+1]) { + if (S->warning != NOWARNING) { + printf ("\rc WARNING: detected and deleted duplicate literal %i at position %i of proof line %i\n", buffer[i+1], i+1, fileLine); } + if (S->warning == HARDWARNING) exit (HARDWARNING); } + else { buffer[j++] = buffer[i]; } } + buffer[j] = 0; size = j; + + if (size == 0 && !fileSwitchFlag) retvalue = UNSAT; + if (del && S->mode == BACKWARD_UNSAT && size <= 1) { + if (S->warning != NOWARNING) { + printf ("\rc WARNING: backward mode ignores deletion of (pseudo) unit clause "); + printClause (buffer); } + if (S->warning == HARDWARNING) exit (HARDWARNING); + del = 0; size = 0; continue; } + int rem = buffer[0]; + buffer[size] = 0; + unsigned int hash = getHash (buffer); + if (del) { + if (S->delete) { + long match = 0; + match = matchClause (S, hashTable[hash], hashUsed[hash], buffer, size); + if (match == 0) { + if (S->warning != NOWARNING) { + printf ("\rc WARNING: deleted clause on proof line %i does not occur: ", fileLine); printClause (buffer); } + if (S->warning == HARDWARNING) exit (HARDWARNING); + goto end_delete; } + if (S->mode == FORWARD_SAT) S->DB[ match - 2 ] = rem; + hashUsed[hash]--; + active--; + if (S->nStep == S->nAlloc) { S->nAlloc = (S->nAlloc * 3) >> 1; + S->proof = (long*) realloc (S->proof, sizeof (long) * S->nAlloc); +// printf ("c proof allocation increased to %li\n", S->nAlloc); + if (S->proof == NULL) { printf("c MEMOUT: reallocation of proof list failed\n"); exit (0); } } + S->proof[S->nStep++] = (match << INFOBITS) + 1; } + end_delete:; + if (del) { del = 0; size = 0; continue; } } + + if (S->mem_used + size + EXTRA >= DBsize) { DBsize = (DBsize * 3) >> 1; + S->DB = (int *) realloc (S->DB, DBsize * sizeof (int)); +// printf("c database increased to %li\n", DBsize); + if (S->DB == NULL) { printf("c MEMOUT: reallocation of clause database failed\n"); exit (0); } } + int *clause = &S->DB[S->mem_used + EXTRA - 1]; + if (size != 0) clause[PIVOT] = pivot; + clause[ID] = 2 * S->count; S->count++; + finalClause = S->mem_used + EXTRA - 1; + if (S->mode == FORWARD_SAT) if (nZeros > 0) clause[ID] |= ACTIVE; + + for (i = 0; i < size; ++i) { clause[ i ] = buffer[ i ]; } clause[ i ] = 0; + S->mem_used += size + EXTRA; + + hash = getHash (clause); + if (hashUsed[hash] == hashMax[hash]) { hashMax[hash] = (hashMax[hash] * 3) >> 1; + hashTable[hash] = (long *) realloc (hashTable[hash], sizeof (long*) * hashMax[hash]); + if (hashTable[hash] == NULL) { printf("c MEMOUT reallocation of hash table %i failed\n", hash); exit (0); } } + hashTable[ hash ][ hashUsed[hash]++ ] = (long) (clause - S->DB); + + active++; + if (nZeros > 0) { // if still parsing the formula + S->formula[S->nClauses - nZeros] = (((long) (clause - S->DB)) << INFOBITS); } + else { + if (S->nStep == S->nAlloc) { S->nAlloc = (S->nAlloc * 3) >> 1; + S->proof = (long*) realloc (S->proof, sizeof (long) * S->nAlloc); +// printf ("c proof allocation increased to %li\n", S->nAlloc); + if (S->proof == NULL) { printf("c MEMOUT: reallocation of proof list failed\n"); exit (0); } } + S->proof[S->nStep++] = (((long) (clause - S->DB)) << INFOBITS); } + + if (nZeros <= 0) S->nLemmas++; + + if (!nZeros) S->lemmas = (long) (clause - S->DB); // S->lemmas is no longer pointer + size = 0; del = 0; --nZeros; } // Reset buffer + else { + buffer[size++] = lit; // Add literal to buffer + if (size == bufferAlloc) { bufferAlloc = (bufferAlloc * 3) >> 1; + buffer = (int*) realloc (buffer, sizeof (int) * bufferAlloc); + if (buffer == NULL) { + printf ("c MEMOUT: failed to reallocate buffer\n"); exit (0); } } } } + + if (S->mode == FORWARD_SAT && active) { + if (S->warning != NOWARNING) + printf ("\rc WARNING: %i clauses active if proof succeeds\n", active); + if (S->warning == HARDWARNING) exit (HARDWARNING); + for (i = 0; i < BIGINIT; i++) { + int j; + for (j = 0; j < hashUsed[i]; j++) { + printf ("\rc "); + int *clause = S->DB + hashTable [i][j]; + printClause (clause); + if (S->nStep == S->nAlloc) { S->nAlloc = (S->nAlloc * 3) >> 1; + S->proof = (long*) realloc (S->proof, sizeof (long) * S->nAlloc); +// printf ("c proof allocation increased to %li\n", S->nAlloc); + if (S->proof == NULL) { printf("c MEMOUT: reallocation of proof list failed\n"); exit (0); } } + S->proof[S->nStep++] = (((long) (clause - S->DB)) << INFOBITS) + 1; } } } + + S->DB = (int *) realloc (S->DB, S->mem_used * sizeof (int)); + if (S->mem_used && S->DB == NULL) { printf("c MEMOUT: reallocation of DB failed\n"); exit (0); } + + for (i = 0; i < BIGINIT; i++) free (hashTable[i]); + free (hashTable); + free (hashUsed); + free (hashMax); + free (buffer); + + int *clause = &S->DB[finalClause]; + if (clause) clause[ID] |= ACTIVE; // temporary + + printf ("\rc finished parsing"); + if (S->nReads) printf (", read %li bytes from proof file", S->nReads); + printf ("\n"); + + int n = S->maxVar; + S->falseStack = (int *) malloc (( n + 1) * sizeof (int )); // Stack of falsified literals -- this pointer is never changed + S->reason = (long *) malloc (( n + 1) * sizeof (long)); // Array of clauses + S->used = (int *) malloc ((2 * n + 1) * sizeof (int )); S->used += n; // Labels for variables, non-zero means false + S->max = (int *) malloc ((2 * n + 1) * sizeof (int )); S->max += n; // Labels for variables, non-zero means false + S->falseA = (int *) malloc ((2 * n + 1) * sizeof (int )); S->falseA += n; // Labels for variables, non-zero means false + S->setMap = (int *) malloc ((2 * n + 1) * sizeof (int )); S->setMap += n; // Labels for variables, non-zero means false + S->setTruth = (int *) malloc ((2 * n + 1) * sizeof (int )); S->setTruth += n; // Labels for variables, non-zero means false + + S->optproof = (long *) malloc (sizeof(long) * (2 * S->nLemmas + S->nClauses)); + + S->maxRAT = INIT; + S->RATset = (long*) malloc (sizeof (long) * S->maxRAT); + for (i = 0; i < S->maxRAT; i++) S->RATset[i] = 0; // is this required? + + S->preRAT = (int*) malloc (sizeof (int) * n); + + S->lratAlloc = INIT; + S->lratSize = 0; + S->lratTable = (int *) malloc (sizeof(int ) * S->lratAlloc); + S->lratLookup = (long *) malloc (sizeof(long) * (S->count + 1)); + + S->maxDependencies = INIT; + S->dependencies = (int*) malloc (sizeof (int) * S->maxDependencies); + for (i = 0; i < S->maxDependencies; i++) S->dependencies[i] = 0; // is this required? + + S->wlist = (long**) malloc (sizeof (long*) * (2*n+1)); S->wlist += n; + + for (i = 1; i <= n; ++i) { S->max [ i] = S->max [-i] = INIT; + S->setMap [ i] = S->setMap [-i] = 0; + S->setTruth[ i] = S->setTruth[-i] = 0; + S->wlist [ i] = (long*) malloc (sizeof (long) * S->max[ i]); + S->wlist [-i] = (long*) malloc (sizeof (long) * S->max[-i]); } + + S->unitStackSize = INIT; + S->unitStack = (long *) malloc (sizeof (long) * S->unitStackSize); + + return retvalue; } + +void freeMemory (struct solver *S) { + free (S->DB); + free (S->falseStack); + free (S->reason); + free (S->proof); + free (S->formula); + int i; + for (i = 1; i <= S->maxVar; ++i) { free (S->wlist[i]); free (S->wlist[-i]); } + free (S->used - S->maxVar); + free (S->max - S->maxVar); + free (S->falseA - S->maxVar); + free (S->wlist - S->maxVar); + free (S->RATset); + free (S->dependencies); + return; } + +int onlyDelete (struct solver* S, int begin, int end) { + int step; + for (step = begin; step < end; step++) + if ((S->proof[step] & 1) == 0) return 0; + return 1; } + +void printHelp ( ) { + printf ("usage: drat-trim [INPUT] [] [