From d5c3a551fe91f13383fd461b2189bd252b746c4a Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:50:33 -0400 Subject: [PATCH 01/24] Stage hash-pinned 100-point blitz patch (1/10) --- .delivery/100-blitz-source/00 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .delivery/100-blitz-source/00 diff --git a/.delivery/100-blitz-source/00 b/.delivery/100-blitz-source/00 new file mode 100644 index 0000000..a61b888 --- /dev/null +++ b/.delivery/100-blitz-source/00 @@ -0,0 +1 @@ +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 \ No newline at end of file From fc3a7fc0718bd8b8521f791dd397eeb57bd1fefa Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:50:58 -0400 Subject: [PATCH 02/24] Stage hash-pinned 100-point blitz patch (2/10) --- .delivery/100-blitz-source/01 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .delivery/100-blitz-source/01 diff --git a/.delivery/100-blitz-source/01 b/.delivery/100-blitz-source/01 new file mode 100644 index 0000000..0c73fe0 --- /dev/null +++ b/.delivery/100-blitz-source/01 @@ -0,0 +1 @@ +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 \ No newline at end of file From 82165071c10c0f4e331f7266da6b584766a8cc81 Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:51:22 -0400 Subject: [PATCH 03/24] Stage hash-pinned 100-point blitz patch (3/10) --- .delivery/100-blitz-source/02 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .delivery/100-blitz-source/02 diff --git a/.delivery/100-blitz-source/02 b/.delivery/100-blitz-source/02 new file mode 100644 index 0000000..5134fa9 --- /dev/null +++ b/.delivery/100-blitz-source/02 @@ -0,0 +1 @@ +RjCke1I1/TIF94mGU/MAxkNVDU406f2YjOVVUoeEXhBg8bPLqxMtXjPTImWcZcrZVvEX9gD0Q0KcMrYI0X95LUrD6+9sBBM6xMtdL4413+GLr7y36B91GyndrYOS55nBQM/dnftbpVOyNlFj+oCUm0V3xBwNsFQXPCX8Dpxhs48p2iCaIobRmxZ6Hp49tTrkFSCmsIpp9xsDxJoMYfYtdeHHRO48rYdrAedzsQdkmIa9jeLXl31cIf16oqfGdIUEup9eCeA7LbUIVWSULIq5ba3DyoSV/qUKXoBXhRb2TTfU9Xh8z0IesexuoqD8gvxLwIAAE75uk6TJ6+Ywgo7caAgw5/8cB6Fd7uqtZKb7QpMjEXUbRsftUQa/AR713v5+iFwJOOMJnnAOKIIVq5ESRcL+sgktcOZGGfFDipXsvTJHp8T4PvZte7wHH1+bSW3HxkNUB+HwxriaNZGjcsz1IrH3nvPYUEHx5QsjcfmHBSkPYniT84vdK+E5qq5uA10t7o8cFjDL5QTSSF310uEtsNIp9NY0KQDOT+6Mw3D2WIlMv5ZlPVkLHLgk8eGaAIKZR5qLYwXbx1Vn46ORO5sAf50WDhkNAL6IXJcNJ9jFrzG24DfAv5UtW9n0prqk6/U32AweA+Sc9qFo7yH91/8X8tiDOzRSvYJmDGZLwC4RHTOowlTJnfgMOL1xpHzPAAKA2lJxqWGh5zNDx6lpDHstn/lJGzQ4ov3yctgAlu0iieg1BWdmP7xPllv+JLI4exr2gmI72vm9XQ81dLppyVpw5UAyaz5QXJnksayw7vqiEl6CIonpo506rRwJ3x546+hW+F4/0w8ibhQ3RHDw764e2eaalnjHm4y8x0nwjFOGLv4Q/AI8dOHNPKUKPGoIJE/MnR0KMWjzAx4qEDS7U7Hf/0WpJ7FDgRCzHcOeH3kgvaIHWMOsI9NG3lX4/QUr64J599b5tsv+3wgRhP8tecsb7Mqc5i7a92/B0VuH/Lnk/nYYT1E8f7RObiBCPOkqsfJQ5Z2KLdHoyn7lDf0H6CvuS+kg53CmJ5nBXSBsUsOX9XbHTtEhvw/X8EtmNA+2Av0bOfhFLz99PZkcZVlxFosL1CearuhFazdsXc3x9t75D1xTeOP2DjvY+RDU9qjxyp5wAv9lciL64bRHNmL37TBMPNWlO5dvaItp7iyCuXCbK1yR5LWEet5a6LAebiy6QSMGBwMYOWl3yWN/ibW9HMPEQXs1iofIfiSU1mSJ2EsXBdfoCb3Gh9eSokUutMvpk+XfiVdEaWbXHWFt775iEFbVWjaodmKMQ87VxXAypvE5wABI/A94L00PsOtMFio1JxX1qbbU0+FJeUFkB54KyIwvM1e9vt2lSBoYWxj2aIKxfLtLpzi1QDzt3iqdi10uH2UQ8jgw+LkELXN5q62y/BV4F+ZuJN0OM3oX3EZ+nviujZE7r1VAW/Hrrx9OcKJBTGO3fCoKxzJSmdddi/phq1onvNNjcUtnIcjtfjqCBazNC50j7gSlauD9FgP8XEMJ2k+PQT0DNRnoRRXxO4Y3E5T/Ai3Mi514SufzStW26mzx8DR9UKs8XW3VdoIle0a16g9PxVAhTtgJD8NciAlig8WRcsFNr740/KnzmdzzjCv28320IgskogYkQKnnmAfBCn6Qg+O0Alcdwy5LEL9g1IpyutbOivarZdUBkM7XGoPgLHpQLewgVGdLjH0OE5UEC9khOy3/iSvipGinospAAgQLaRaFnnICkmU8VTR+CgK9aZSVVJOu8xipmFvPCM42bbPave7AjeTDylplN7T15hGhHsGL96evGnW64lraSP0crgV+KlAdHFMIZbOGgf6tK6BMm1B1BeI1m14Y3xQFGG9DJXWMNNC7v1GSya1nrA7T/7VW10qlrJMeKiEV7cuQ6ps6dS16UT3u+T3p//HWv6pkOomvOyxBxV52C1gSGJkVvJ9q1s3PzinICFd42FKZ7wVoAYuIL+Rtigqshx++HEYHwPKjKO29nftsOOu1AlXokrjnE0gtZW9BSF7kFoevJBk999S9vx3Z1F+8YKColm6dY+EdBHlaBoi2aBPrlCv4hTIwnK6ArOkcWenZRGiKKjpLTGE9VUeHR3R4TKdcmLoR5+PwwbRvP2RrfzHjVLT4u6Mci9VibjV+PHArhcfQKUA8P1Vr1r18gTqOXnoGW5QDGH2/bTJJekma0623kA4v4AFYaEhFP3OJo4f8NoWltcqBA84vgaW5DSPe3lQo9VgCLOtUy8N9/BWlSum5ODndIH8iGSEiary/1BVOvjC8ZkaicGMLznRRssAWjvdNFCq7Mt9aIW35zPMjDag6Pup1Bp+I0JDl6+S7OkGzrPvkIEovfIQz8dhKy5ijZPw3hr9pqKirBy3DIA0dsgNSE6oz4dI+gG4cg9OMcUwI7p9Gsby6/R7PlHNrSQVjVLFplyD+OSYsfZwH28bzdx8mKjWgQEmyiLmYk82rwFJLVfz4f6VoPtf7sX76k2dY1RQ8p4m/BKUH8IxBlj7jBanwh9V5dq2FWa+AEl6XIWyOgBNY1pwh6cjwRNvMOHIvJLmUufxGfqQM1+dZZZ/RzHB/K9hytUPczu5/LbzcUj4TtFFh9edKZzIB3MUh8c8rq+kY1CM/W+u6yLWrjOz+wWm+SzqcMjQpzjt6yiHc2gilGU+fhGeYDxxPBZsz/HAnNpUbXqI2wtbf5eJOJca7c6+t0oaPDIyvEX3uq/jHv1/K1/c33qSCQTsJjlSV5whLFrDxmHZT4CZ4wKHHWDIhRwMpFfa5bSiKHLEHdqA9lSZ/kPHrRbtTuyiOB4Z/uP8/V/3HBqI/V5rRKhFDDdDSxrDSkiXGfOk/pwfrCC5ImZCSR8vQMrSePBDh2RHZj+UmJZ1fuBAov+N4lI5Yll0kHMkpmYQXmtUElQb21SKLrLlP8+bMcAUV0Mantrrkg3bjqU7Ffg+cxTlXMHLR3+VTVG/j9y/YIZakwcyncO/qE03Jt6IkEAmCVuglQ7sZOdTA3iVeS383DZCVN00XHrsoP0ukfBtVlQW/LZsPPSFF4YYRzfiQA1JnvFl6X1s1TMLpKRztSOQ5ZYd+5C1mJA+SZm64s0GzHaFHTjpRRNd2DFh+qmjIKbAQ6BPbeQIViK+1L5RSzu9r5siS/TOzRq0+8lAMOO5lxQ4Nx8BektarqBcWfagosbchfqyu2Lefdlh9LMRHL5BJDQytnNo9kM7NtDmobKqD/Mnct8WfL9t4EXwh+GH7IGJBQjs6TFei4TY80AkpqFq2rXQEJjblOw/fg1XGzbWO9ZkKSuZ7KgAeYW7vPxOrKS6iXfb2Y49ejpWXKa85Os/OuCjqSc8ObV4x4d/cQBU1MjsSG1M/7JpHLiDWJQfZ9X743nBjBS8lZmGWmflkeE+x63y8ZN6ABNOPk4RAcOsdocWWTG3uZ1JwzNdgO9A3XpItpVpY4GLogGNYNuCsgvpaHaAsghw21g1X7xq6JO0VLZunFADsqZb1kNZGHPjrbQ/hNQ2pEnIYRJJrLfgKXT+AJxlqwYVH9xZxtYTph7fYyuHcL3BQ4lvzBfOWcGmtUKnpRf++UDmN6KUDSbwSHxPGFMZSMro0OKbXl5fMTwclSh3nvWXGwLdUgL3YQFjn6nBPfxKUnksekI5FGA/sk/DIutLmSMjmDGlXvYQuHudfhH31v94jH3OH6pbaMPcFEB697zjw3iDvpiZsuilIt358GUN22MHq+YsjdQQJ/qmg2jiVa5uOkm2FOSKM52BefjZfXsTLrn5YzbgUpUbC7t+Prqc7ZDq6m9nSYwe+Yns+6K+9uo+H+VvR6KIUCwqU1EfCJR09048wW2nHi+dXzwc7Bgo4uss0OqxhVmXdeOyX7bXcpDdzsqKi97r2g4gN1T1oq1OWW2croN8LYRgaX2wGaBboRkEeXy0/wGp8U6i5YUoasn4LZP4HyU4OuWjgDspJETRbgmnPweTuHxDALfv8JKnWHoPfksVR \ No newline at end of file From 3339da733cb74faf791d3655db8255687b673df1 Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:51:48 -0400 Subject: [PATCH 04/24] Stage hash-pinned 100-point blitz patch (4/10) --- .delivery/100-blitz-source/03 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .delivery/100-blitz-source/03 diff --git a/.delivery/100-blitz-source/03 b/.delivery/100-blitz-source/03 new file mode 100644 index 0000000..0369713 --- /dev/null +++ b/.delivery/100-blitz-source/03 @@ -0,0 +1 @@ +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 \ No newline at end of file From 40ee93bf9190e24ccc15ab34f3f82ed32ebf3b04 Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:52:52 -0400 Subject: [PATCH 05/24] Stage hash-pinned 100-point blitz patch (5/10) --- .delivery/100-blitz-source/04 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .delivery/100-blitz-source/04 diff --git a/.delivery/100-blitz-source/04 b/.delivery/100-blitz-source/04 new file mode 100644 index 0000000..16f7643 --- /dev/null +++ b/.delivery/100-blitz-source/04 @@ -0,0 +1 @@ +IpDCUkZoag8mMJDKp3iugAtbXa2X0/p4JQ2LX6Dak2yr8ynTlq+U5RhA/ufgzZsg92STziAX+guJJd4naGOA6cC4A7tmaFd2oQ8ywuZGdM5Jvo+2zAFVq3zK47+Xz4WBYBlRGk3uPSGjrYOZOWOfCqx9IMDj4/egBjJldrnIebJJb9+MKNjGv2o6Hn1rGL4kQJgZnd9R9d7X7e1fmKUifBUb94PPonuspa79O0hMCkmVYq1UkmPfY5WhnITgxUlBPhNeexvwiSLeXeBab+hAcGNt4QJ4/qXS9CujXjmw+sUhvVC9nfWl4BieK0FtP0eTq1MgSfLXyKjyPIcPGvmF6dNwbekm2r6LamaZsA2xfy36csSRB3fDKR2/ms39zALnduHQ1mfOxZdG3ffT3t5n8wStfLs6B4R/4cOzJwqnbAlA9GBqtTL4vilhjU0dberEmiaDEC91n7NnaWHnc1dLK8JtHFIDk3GZx6r8OcRkMUyfc6fylVx54c/enteztPPldvSAz7y6fYvxi80ZROCg5oyfrjW6o9V5IUjt84zqCrVMrHSsniEw09X0fKSQtiYyUQr2GWWvaaVokVu3rYEdG9Tpx+/QtHEnsMXTdOinnCJLERs5yPOKbkpM8Dj6CmnRUbu7ZbfhMVuiXocc66Tk86yp8Q40N+y0JOZTGyXUSYJnEYbi7H0xFBqupCAwl22mmxTy2e7xHEB/mhB7JaFiPLNmDa1wF8MI5Qlx/mXhwCp2M01UPgs+IF/mnX9FgGcC2EFDUceABMstTNgzv04z9CKx19oF9fXL0nC/5v/l2Q1EWi40L6tp5VIi5qaPOnB1CdwIY0gYYWCRPHXzOvHo6KYmAfFgHt1SCJIYkMBwSBJSixU/IQrLwHYyWL7wZsr5QtXtUdLPsqw3PtGK2i3QneqCZUV+s+Qc5nU2YVeIvU8lspNs5fHxl7r2RV9fBTQfVMO1eI3JvJ70ggpV+5/2ZdwAVxNg++Oda0QuW7yvIYPoSnjtVWKgxm7TvA3SSH7qrm4XMb3eHTLT3AxjOzq0P80SG0fR2fn8o8gnmi5HuIZNG4xwifnKc8zERspyjsopeX1I59gsqoNrG0k/zEY7/YDWlG/yhzEDhSsqUe1HzwN2g+tUXWN4eKLf54XOMgp8bA8HiF4FzrSr4mHED57F+ruz3IaKKpS7Iuyeixb+7RtpqpliaDBjjw/jj+PDL6duNyKvLoQUP/+yIQR9epnzmg7yupN+Kfi86a/swAEoAJ6Bwo7DO4kJb9TKVXlzvuWmQ/Vjdjq/c+wLUeXS+/fwIDn9l2VRDgWoeZJTblBP/wkQY6n8NumHKR/la/fQBjdWRkzFZAVmEKYOyJS2m5jPx1MJOGbfn5Vyn+ahd14Z/008kRGUit33GVXqrME80i6oZIHeSBQqrcfJpsae8zU5ZuNezSn+AOkmfEOifI/zxQPgStzcKU1dJNKoEyqdzKW8kl3F0eqMKG+8m+rYjjiZHjGN2pcfbLJ/Ti8R9GXKYR/aM3JY3PTmS7hC60lvm6YZ4C0aqrSG9qN4PwP1bw7Rgrs3T64BhZQIIm9MhyMvqmVxOKxxzSBVhALNEAJYGXEiejbMc6z8IlUmn+wvU8+40OixBV+WfmMi0/ahXVQ5aKnt/L7axp5tPBxq/GgVdJu5aP4U0mNsVeD1Oru9d98JMuZVW1wvqPY5I4ohb7TObMhbbbVgaA/fuUtNQc85V+4Ehpa3ktLhwDGL05u/5GlWt1Y6FIFA5dxJ9J8ah5SKRFn/ZgOghxa1hJenfHQRfaoFs7ck1OE8ag3mYYb2kvrXhM9S5vgVONeJZSxun99iDU6E0Px6EKVeE8CEaa8xSEj0L8MLSRW6WDs0dajn+LIMrMeUgx6OTXh9N5x8LxakO18uEeS67iW2M+rndwa4UStC7Y0jLPLp1Et/2gfNJ3FYoZorqLJwK5kMUNJfL2gTOfMuqGCXOi60qpYQAJiFdm52Zdy92tJpFaygLVSWw5+ouoM/MZIgA341rHkoo99Y+uE6+nPkYldrG1Vynj60bPqkhevkAy4a7G87j6nPROhiJ0XRDvWCH7avnhPO+hMn11bgngvbShgqi0WXpEyguEPFZMxmaoCsnbWE+bgx/8hmpMa2v5jQEl+fwJBmRocIxPv9a26AVKrGR3sMy2BNSj5n32vNbr+lgFOh3NyV9gW0hbF/OY04v4fKhjtDw8TjcXHS8a+a1jOcwepyu3o5IXA2ILSwkfv8EsQyuczl5HhB+qiq8N4VvhtxgOkVzGg+17WYepglk9Ft29F5dKIdXJ0iVJEy4uhJ/ojgXvm/pf/61gDWCPOwRcVKglNEyzyrbKEnP/v29taDtYv+XIVV7lFH1ps5XBUcEMA85aYYx+2pVsXMP3ADG6Xy266elOE+8sdPN9EmCVrH8IqTTh53vyAovUhzV+XAWAWjKOnQOnDtlVDN7v1qAyS12dK1rdvZVX0fp4WZ/c9KBfGfb+natQGm4B/DgWZRF8/tZWrcFOdpyMBeHAB/FSBA9HQXxGe5U++hwxiSDhPDEOVul2xaO8Qc/5XOKr/e9WCxNVD8IOiDbbohJ7/A4PaXd2SceFUi0oBJPLEJw3esvKcWcWiVGZE7J6E9F+Qqqn05TokbDJt1k3tMwB9T5NNpern9O8wscp8hSYQGFmMvdURneWbFcHaAph7OsPw0HbJFkFpV18l+TLDaAA318WiRZzSoVWmAOqKvBWKAr9BiKxZQmzciyhOUPBSjDt78nDNsmBlOal32rGZWAZK3/dLkGeu4eaNs6Z7ud4hp8MCBMvWssnCggej9iCWv5mSJ1y+ac9jw0zxq6hzR6OUsmJS5X/ZXLL7yiFPAN5uvOEXafPOBHJ1iUAJwSgbYjMyoZlCi4V0LUkb2nkdfWlmzolYkJ56aGPDKiHsQ+PxBzoPOSGcZWxCThiaqPv5Qu5S/NI1k5sliInZ/72rnZnUV8FbxCxMzgzWw73LzDpnnvjwHuWelSc+itckW8Lb5mEGZ/M2dg+g18WPPh3QuhHW3UyalJMYX4K5Sxe3VLmu7WOSXWjI/yFINrV/v6XoqLOl1sel7AVa1CqlBtvgsnjM5bClo82uN1Ync1OUy3B4sYFG1CEnLX1zBOH3UtqnPTSKm3WcuRM8oFIBfgdwp5RUS2hiZQ7c++kpXG02gD6Gx5EPGMkB4039lbi0PBG1pyK+TTDEgMFHeovUIo6e8ONE5WR5Eif4K40741RZDjfh2wBGZuDlmUYTEOwbfVrtyVTStTjyAx0AW2PYeRmvBZNnvjzyjWRjUrrFKuKof6QIlZ2wmZqpG9/2iG6pTbo8x6/3Y4qj1mOObKYUX25uxXF3B/m41UvBAIBDbEUUi1U5TQT3LfZAcdIDC2QKxC6ASpDnhGTisVXX97iZlOxTxwI/RV6GIBe7MjYyDO9r0bIx2bkD8XuQICsb3GMocL5VOUsJu0nTTAeRGIAi/xReUhANCQ6UTk4bNUwWLTxI9bRawnnDQQRAotjI1phSKhqqpfj8fN5LTKXYCgQ/5haGTHYy46tSZeTt392vSicjXizjvisobiGRbduz/wy86bgNkOjnBtZBS489md0K+0ENXrL60VG5osCUFA97pgKfeeQFLxsQlpAAjwNJZgqXK2836Bfk1ghYPm7BOwekUh9spkKmVsYXvGuGVbipZS85/lMVEAoyt0n/XpqRTDLSHkVQxzjun1tEjuKkB8PdCUtRnWhyWtgqGzezfTH+Xn33VlB/ktCD/pfIINuwu0nJ5CbcuYVdN4zIh8r/uV68gmrS9R4RTdog0mC24UzC5rGYWiJgOVtnF7c//QQ/1OjJK3hLBhLgXYW67irdqcGsFc8vjVPUzGG6Y/YNVG4lXPhyfZmWv588ULkuGWjAuFnStiDiXE1vw6fuN4U3o+DXMYY5HJE5mFP2t1RBnCVKnztSUiifbiW9QOPkRYnhRPARa3h0Ho4zS3SQN54mbknZN+HfB//dYCb1H \ No newline at end of file From 8a6c42128a6dcbc2e8bc7e489669de8ff4bb601e Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:53:19 -0400 Subject: [PATCH 06/24] Stage hash-pinned 100-point blitz patch (6/10) --- .delivery/100-blitz-source/05 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .delivery/100-blitz-source/05 diff --git a/.delivery/100-blitz-source/05 b/.delivery/100-blitz-source/05 new file mode 100644 index 0000000..0a76cd8 --- /dev/null +++ b/.delivery/100-blitz-source/05 @@ -0,0 +1 @@ +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 \ No newline at end of file From 4f8ad658b258de2b0636248985ec721fb3d6f403 Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:53:41 -0400 Subject: [PATCH 07/24] Stage hash-pinned 100-point blitz patch (7/10) --- .delivery/100-blitz-source/06 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .delivery/100-blitz-source/06 diff --git a/.delivery/100-blitz-source/06 b/.delivery/100-blitz-source/06 new file mode 100644 index 0000000..f880401 --- /dev/null +++ b/.delivery/100-blitz-source/06 @@ -0,0 +1 @@ +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 \ No newline at end of file From de318e54520cd1cc08fd03108b2461469ae85db6 Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:54:08 -0400 Subject: [PATCH 08/24] Stage hash-pinned 100-point blitz patch (8/10) --- .delivery/100-blitz-source/07 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .delivery/100-blitz-source/07 diff --git a/.delivery/100-blitz-source/07 b/.delivery/100-blitz-source/07 new file mode 100644 index 0000000..46cfce7 --- /dev/null +++ b/.delivery/100-blitz-source/07 @@ -0,0 +1 @@ +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 \ No newline at end of file From baee76ed44a647cd08121319fdf8e1b5416d03fc Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:54:33 -0400 Subject: [PATCH 09/24] Stage hash-pinned 100-point blitz patch (9/10) --- .delivery/100-blitz-source/08 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .delivery/100-blitz-source/08 diff --git a/.delivery/100-blitz-source/08 b/.delivery/100-blitz-source/08 new file mode 100644 index 0000000..3f72158 --- /dev/null +++ b/.delivery/100-blitz-source/08 @@ -0,0 +1 @@ +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 \ No newline at end of file From 6f8f0d14e03df5b3f69f4ded0a973f8daa4047c5 Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:54:56 -0400 Subject: [PATCH 10/24] Stage hash-pinned 100-point blitz patch (10/10) --- .delivery/100-blitz-source/09 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .delivery/100-blitz-source/09 diff --git a/.delivery/100-blitz-source/09 b/.delivery/100-blitz-source/09 new file mode 100644 index 0000000..b8e6d09 --- /dev/null +++ b/.delivery/100-blitz-source/09 @@ -0,0 +1 @@ +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 \ No newline at end of file From 8a783ac63c816b4691e6e2fe144b27fdca0a44bc Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:55:16 -0400 Subject: [PATCH 11/24] Publish the hash-verified 100-point blitz checkpoint --- .github/workflows/publish-100-blitz.yml | 81 +++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 .github/workflows/publish-100-blitz.yml diff --git a/.github/workflows/publish-100-blitz.yml b/.github/workflows/publish-100-blitz.yml new file mode 100644 index 0000000..f2d5dd8 --- /dev/null +++ b/.github/workflows/publish-100-blitz.yml @@ -0,0 +1,81 @@ +name: publish-100-blitz +on: + push: + branches: [research/100-blitz-20261016] + paths: ['.github/workflows/publish-100-blitz.yml'] +permissions: + contents: write +concurrency: + group: publish-100-blitz + cancel-in-progress: false +jobs: + publish: + runs-on: ubuntu-24.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Verify and reconstruct reviewed patch + env: + EXPECTED_B64: f75650c04f59e0ae6e02d2190ebd56668442d790276d5cc9fa3dc11d188108a7 + EXPECTED_XZ: 31780857b8f024c4cb4f1144d04f8e8fdebf8b902ab1acd7c293673aacaef036 + EXPECTED_PATCH: dcd47a5a9a621f9f20d91cf040cc125c5300089b699dbacfbd25a2be84e83ab6 + run: | + set -euo pipefail + mkdir -p /tmp/blitz + cat .delivery/100-blitz-source/{00,01,02,03,04,05,06,07,08,09} > /tmp/blitz/patch.xz.b64 + test "$(sha256sum /tmp/blitz/patch.xz.b64 | cut -d' ' -f1)" = "$EXPECTED_B64" + base64 --decode /tmp/blitz/patch.xz.b64 > /tmp/blitz/patch.xz + test "$(sha256sum /tmp/blitz/patch.xz | cut -d' ' -f1)" = "$EXPECTED_XZ" + xz --decompress --stdout /tmp/blitz/patch.xz > /tmp/blitz/reviewed.patch + test "$(sha256sum /tmp/blitz/reviewed.patch | cut -d' ' -f1)" = "$EXPECTED_PATCH" + - name: Validate base identities and publish atomically + run: | + set -euo pipefail + test -z "$(git status --porcelain)" + test "$(git rev-parse HEAD:spectra/exact.py)" = 983d33551e29d13016e36cd6038d02d270575b66 + test "$(git rev-parse HEAD:spectra/_native/exact_binary.cpp)" = 96182ee6c7754d24e1c8bc46181dd69a5732d32f + test "$(git rev-parse HEAD:experiments/compiled_query/exact.py)" = 780a7ff1d8f293450fb592ae9e29e129e92a551c + test "$(git rev-parse HEAD:experiments/compiled_query/exact_native.cpp)" = 96182ee6c7754d24e1c8bc46181dd69a5732d32f + git apply --check --index /tmp/blitz/reviewed.patch + git apply --index /tmp/blitz/reviewed.patch + git diff --cached --name-only -z > /tmp/blitz/changed.paths + python - <<'PY' + from pathlib import Path + expected = { + 'docs/EXAM_REPAIR.md', + 'experiments/compiled_query/ITC_EXAM_SUITE_PROTOCOL.md', + 'experiments/compiled_query/ITC_EXAM_TRANSFER_PROTOCOL.md', + 'experiments/compiled_query/exact.py', + 'experiments/compiled_query/exact_native.cpp', + 'experiments/compiled_query/itc_exam_suite.py', + 'experiments/compiled_query/itc_exam_transfer.py', + 'experiments/compiled_query/test_exact.py', + 'experiments/compiled_query/test_itc_exam_suite.py', + 'experiments/compiled_query/test_itc_exam_transfer.py', + 'scripts/check_exam_repair_installation.py', + 'spectra/_native/exact_binary.cpp', + 'spectra/exact.py', + 'spectra/exam_repair.py', + 'spectra/itc2007_exam.py', + 'tests/public/test_exam_repair.py', + 'tests/public/test_itc2007_exam.py', + } + changed = { + item.decode('utf-8') + for item in Path('/tmp/blitz/changed.paths').read_bytes().split(b'\0') + if item + } + if changed != expected: + raise RuntimeError(f'changed path set differs: {sorted(changed ^ expected)}') + PY + git rm -r .delivery/100-blitz-source + if git diff --cached --name-only | grep -q '^\.github/workflows/'; then + echo 'Workflow changes are not permitted in the reconstructed code commit.' >&2 + exit 1 + fi + git config user.name 'Tugrap' + git config user.email '106766396+tugrapaydiner@users.noreply.github.com' + git commit -m 'Publish proof-carrying exam repair and prospective ITC suite' + git push origin HEAD:research/100-blitz-20261016 From 3036a3c97cf5706d60ab9e277708081b4f241954 Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 18:55:27 +0000 Subject: [PATCH 12/24] Publish proof-carrying exam repair and prospective ITC suite --- .delivery/100-blitz-source/00 | 1 - .delivery/100-blitz-source/01 | 1 - .delivery/100-blitz-source/02 | 1 - .delivery/100-blitz-source/03 | 1 - .delivery/100-blitz-source/04 | 1 - .delivery/100-blitz-source/05 | 1 - .delivery/100-blitz-source/06 | 1 - .delivery/100-blitz-source/07 | 1 - .delivery/100-blitz-source/08 | 1 - .delivery/100-blitz-source/09 | 1 - docs/EXAM_REPAIR.md | 62 ++ .../compiled_query/ITC_EXAM_SUITE_PROTOCOL.md | 91 +++ .../ITC_EXAM_TRANSFER_PROTOCOL.md | 127 +++ experiments/compiled_query/exact.py | 12 +- experiments/compiled_query/exact_native.cpp | 47 +- experiments/compiled_query/itc_exam_suite.py | 352 ++++++++ .../compiled_query/itc_exam_transfer.py | 752 ++++++++++++++++++ experiments/compiled_query/test_exact.py | 27 +- .../compiled_query/test_itc_exam_suite.py | 76 ++ .../compiled_query/test_itc_exam_transfer.py | 179 +++++ scripts/check_exam_repair_installation.py | 89 +++ spectra/_native/exact_binary.cpp | 47 +- spectra/exact.py | 12 +- spectra/exam_repair.py | 418 ++++++++++ spectra/itc2007_exam.py | 316 ++++++++ tests/public/test_exam_repair.py | 146 ++++ tests/public/test_itc2007_exam.py | 85 ++ 27 files changed, 2805 insertions(+), 43 deletions(-) delete mode 100644 .delivery/100-blitz-source/00 delete mode 100644 .delivery/100-blitz-source/01 delete mode 100644 .delivery/100-blitz-source/02 delete mode 100644 .delivery/100-blitz-source/03 delete mode 100644 .delivery/100-blitz-source/04 delete mode 100644 .delivery/100-blitz-source/05 delete mode 100644 .delivery/100-blitz-source/06 delete mode 100644 .delivery/100-blitz-source/07 delete mode 100644 .delivery/100-blitz-source/08 delete mode 100644 .delivery/100-blitz-source/09 create mode 100644 docs/EXAM_REPAIR.md create mode 100644 experiments/compiled_query/ITC_EXAM_SUITE_PROTOCOL.md create mode 100644 experiments/compiled_query/ITC_EXAM_TRANSFER_PROTOCOL.md create mode 100644 experiments/compiled_query/itc_exam_suite.py create mode 100644 experiments/compiled_query/itc_exam_transfer.py create mode 100644 experiments/compiled_query/test_itc_exam_suite.py create mode 100644 experiments/compiled_query/test_itc_exam_transfer.py create mode 100644 scripts/check_exam_repair_installation.py create mode 100644 spectra/exam_repair.py create mode 100644 spectra/itc2007_exam.py create mode 100644 tests/public/test_exam_repair.py create mode 100644 tests/public/test_itc2007_exam.py diff --git a/.delivery/100-blitz-source/00 b/.delivery/100-blitz-source/00 deleted file mode 100644 index a61b888..0000000 --- a/.delivery/100-blitz-source/00 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.delivery/100-blitz-source/01 b/.delivery/100-blitz-source/01 deleted file mode 100644 index 0c73fe0..0000000 --- a/.delivery/100-blitz-source/01 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.delivery/100-blitz-source/02 b/.delivery/100-blitz-source/02 deleted file mode 100644 index 5134fa9..0000000 --- a/.delivery/100-blitz-source/02 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.delivery/100-blitz-source/03 b/.delivery/100-blitz-source/03 deleted file mode 100644 index 0369713..0000000 --- a/.delivery/100-blitz-source/03 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.delivery/100-blitz-source/04 b/.delivery/100-blitz-source/04 deleted file mode 100644 index 16f7643..0000000 --- a/.delivery/100-blitz-source/04 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.delivery/100-blitz-source/05 b/.delivery/100-blitz-source/05 deleted file mode 100644 index 0a76cd8..0000000 --- a/.delivery/100-blitz-source/05 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.delivery/100-blitz-source/06 b/.delivery/100-blitz-source/06 deleted file mode 100644 index f880401..0000000 --- a/.delivery/100-blitz-source/06 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.delivery/100-blitz-source/07 b/.delivery/100-blitz-source/07 deleted file mode 100644 index 46cfce7..0000000 --- a/.delivery/100-blitz-source/07 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.delivery/100-blitz-source/08 b/.delivery/100-blitz-source/08 deleted file mode 100644 index 3f72158..0000000 --- a/.delivery/100-blitz-source/08 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.delivery/100-blitz-source/09 b/.delivery/100-blitz-source/09 deleted file mode 100644 index b8e6d09..0000000 --- a/.delivery/100-blitz-source/09 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/docs/EXAM_REPAIR.md b/docs/EXAM_REPAIR.md new file mode 100644 index 0000000..304b0b3 --- /dev/null +++ b/docs/EXAM_REPAIR.md @@ -0,0 +1,62 @@ +# Proof-carrying bounded exam repair + +`spectra.exam_repair` maps a narrow operational rescheduling problem to the +versioned `spectra.exact` API: + +- each exam has one or two pre-approved candidate periods; +- exams sharing a student may not use the same period; +- explicit exam exclusions are also supported; +- current period closures, fixed moves, and allowed-period restrictions are + intersected into an exact query; +- SAT returns a complete named assignment; +- UNSAT returns an independently verifiable proof that can be translated into + named exams, periods, and a shared-student/exclusion chain. + +```python +from spectra.exam_repair import ExamRepairModel +from spectra.exact import ExactRuntime, build_exact_runtime + +model = ExamRepairModel.create( + period_ids=("Monday AM", "Monday PM"), + exam_ids=("MATH101", "CHEM101", "HIST101"), + students=((11, 20), (20, 31), (31,)), + candidate_periods=(("Monday AM", "Monday PM"),) * 3, +) +query = model.freeze_event( + closed_periods=(), + fixed=(("MATH101", "Monday AM"), ("CHEM101", "Monday AM")), +) + +runtime = ExactRuntime(build_exact_runtime("exact-native")) +with runtime.prepare(model.problem, mode="scc") as session: + answer = session.solve(query) + +if answer.status == "SAT_VERIFIED": + print(model.assignment_from_labels(answer.labels, query)) +else: + print(model.explain_unsat(query, answer.proof)) +``` + +## Scope boundary + +This is **bounded repair**, not full timetabling. The adapter does not generate +candidate periods or model room capacity, room assignment, precedence, +coincidence, or soft objectives. Those constraints must be incorporated before +admission or checked separately. + +`spectra.itc2007_exam` provides a strict reader for the ITC-2007 examination +format. Its `conflict_core` conversion preserves an immutable receipt of every +constraint class outside the bounded model and refuses conversion unless the +caller explicitly acknowledges those omissions. + +The research transfer protocol is documented in +[`ITC_EXAM_TRANSFER_PROTOCOL.md`](../experiments/compiled_query/ITC_EXAM_TRANSFER_PROTOCOL.md). + +## Prospective multi-instance evaluation + +The selector-v2 policy and its `set10`/`set12` execution holdouts are fixed in +[`ITC_EXAM_SUITE_PROTOCOL.md`](../experiments/compiled_query/ITC_EXAM_SUITE_PROTOCOL.md). +The suite rotates measured engine order, preserves every failed cell, and gates +only the predeclared auto/raw overhead on holdout cases. Passing that suite is +application-transfer evidence, not an operator deployment or full ITC-2007 +optimization result. diff --git a/experiments/compiled_query/ITC_EXAM_SUITE_PROTOCOL.md b/experiments/compiled_query/ITC_EXAM_SUITE_PROTOCOL.md new file mode 100644 index 0000000..b36df50 --- /dev/null +++ b/experiments/compiled_query/ITC_EXAM_SUITE_PROTOCOL.md @@ -0,0 +1,91 @@ +# Prospective ITC-2007 exam-repair suite protocol + +**Status:** frozen before executing the `set10` and `set12` result panels. The +selector may be falsified by these holdouts, but it must not be retuned inside the +same suite. + +## Purpose + +The first application transfer used `set9` and exposed an important limitation: +the original automatic policy selected the SCC engine even though the explicit raw +engine was faster on that dense repair bank. The structural-v2 policy adds one +answer-independent rule: + +> For a bank of at least 64 requests, use raw traversal when the immutable request +> packet contains at least eight pins per request on average. + +The threshold was chosen from the already exposed synthetic development panel and +the `set9` development transfer. It depends only on packet geometry, not SAT/UNSAT +answers, proof sizes, or measured times. Short banks, base-infeasible graphs and +the prior SCC/component rules retain their previous ordering. + +## Fixed external source + +Every workflow checks out `davidvanbulck/ITC-2007-CETT` at commit +`ed2d4c0cf5302b77a8b321cd0380de0bb579d538` and retains its `LICENSE` in each +case directory. No upstream data is committed to SPECTRA. + +The fixed cases are: + +| Case | Role | Instance | Published feasible schedule | +|---|---|---|---| +| `set9-development` | Development | `examtt-instances/Instances/set9.exam` | `InitMethods/SolStore/set9_OBSIFeas_0.sol` | +| `set10-holdout` | Prospective holdout | `examtt-instances/Instances/set10.exam` | `InitMethods/SolStore/set10_OBSIFeas_0.sol` | +| `set12-holdout` | Prospective holdout | `examtt-instances/Instances/set12.exam` | `InitMethods/SolStore/set12_OBSIFeas_0.sol` | + +The bytes are public, so “holdout” means **execution holdout**, not secret data: +no selector-v2 answer or timing from `set10` or `set12` may influence the committed +policy or acceptance threshold. + +## Per-case construction + +Each case uses the exact construction in +[`ITC_EXAM_TRANSFER_PROTOCOL.md`](ITC_EXAM_TRANSFER_PROTOCOL.md): validate the +published schedule against all original hard constraints, freeze exams involved in +unsupported hard constraints, assign one deterministic duration-compatible +alternative to eligible exams, build the shared-student/`EXCLUSION` conflict core, +and generate 1,024 unique deterministic repair events. + +Every event is answered by `auto`, `raw`, `scc`, and `route`. Completed answers +must include a full assignment or portable contradiction proof and must replay from +the original `SBC1` packet without native code. + +## Timing schedule and prospective gate + +Each engine receives one untimed warmup. In each of five measured repetitions the +engine order rotates, preventing one mode from always running first or last: + +1. `auto, raw, scc, route` +2. `raw, scc, route, auto` +3. `scc, route, auto, raw` +4. `route, auto, raw, scc` +5. repeat from the first order + +For every holdout, pair `auto` and `raw` by repetition and compute the median of the +five `auto_elapsed / raw_elapsed` ratios. The predeclared guardrail is: + +```text +paired-median auto/raw ratio <= 1.15 +``` + +This is a bounded overhead guardrail, not a speedup claim. Development-case timing +is reported but cannot pass or fail the prospective gate. Failed holdouts remain +in the artifact and may not be removed, renamed, rerun under a changed threshold, +or converted to development cases. + +## Evidence and audit + +The suite manifest binds: + +- source commit and per-case source inventory; +- upstream repository and commit; +- exact instance and solution paths and SHA-256 identities; +- development/holdout role; +- selector policy and threshold; +- per-case manifest hashes. + +The suite audit reruns every child audit, checks every result and proof, checks the +rotated timing schedule, computes paired ratios from recorded repetitions, and +publishes all failures. A successful result is still project-owned execution, not +outside-team replication, operator adoption, full ITC-2007 feasibility, or +algorithmic-priority evidence. diff --git a/experiments/compiled_query/ITC_EXAM_TRANSFER_PROTOCOL.md b/experiments/compiled_query/ITC_EXAM_TRANSFER_PROTOCOL.md new file mode 100644 index 0000000..59c79df --- /dev/null +++ b/experiments/compiled_query/ITC_EXAM_TRANSFER_PROTOCOL.md @@ -0,0 +1,127 @@ +# ITC-2007 bounded exam-repair transfer protocol + +**Status:** fixed before opening the external instance and schedule in CI. This is +an application-format transfer of the versioned proof-carrying exact-query API, +not a complete capacitated examination-timetabling result. + +## External source and attribution + +The workflow checks out `davidvanbulck/ITC-2007-CETT` at commit +`ed2d4c0cf5302b77a8b321cd0380de0bb579d538`. The selected files are: + +- `examtt-instances/Instances/set9.exam` +- `InitMethods/SolStore/set9_OBSIFeas_0.sol` +- `LICENSE` + +The upstream repository states that its solver and instances originate from the +ITC-2007 examination-timetabling setting and requests citation of Van Bulck, +Goossens and Schaerf. Every evidence package retains the upstream license and +this acknowledgment: + +> This product includes software/data distributed by David Van Bulck, Dries +> Goossens, and Andrea Schaerf, Ghent University, Belgium +> (https://github.com/davidvanbulck/ITC-2007-CETT.git). + +No upstream source or data is committed to SPECTRA. + +## Admitted question + +Start from the published feasible timetable. Each examination is assigned one +or two **pre-approved candidate periods**: + +1. The published period is always retained. +2. Exams appearing in `AFTER`, `EXAM_COINCIDENCE`, or `ROOM_EXCLUSIVE` hard + constraints are frozen to the published period. +3. Every other exam receives one deterministic alternative when available. The + alternative must have enough duration and the instance must contain a room + large enough for the exam in isolation. A SHA-256 ordering of + `(input hash, exam, period)` chooses among eligible alternatives. +4. Shared-student conflicts and explicit `EXCLUSION` constraints become graph + edges. The palette contains the instance's declared periods. + +The exact question is therefore: + +> Given current period closures and requested moves within these fixed +> one/two-period domains, does a conflict-free period assignment exist? + +A completed call must return either a full original-exam assignment or a portable +original-literal contradiction proof. The standard-library verifier replays all +answers from the frozen `SBC1` packet and `SCQ2` output. + +## Deliberately unmodelled constraints + +The reduction does **not** solve or score: + +- room reassignment or simultaneous room-capacity packing; +- soft institutional penalties; +- changes to `AFTER`, `EXAM_COINCIDENCE`, or `ROOM_EXCLUSIVE` exams; +- candidate-period generation outside the frozen deterministic rule; +- the full ITC-2007 optimization problem. + +The parser retains counts for every omitted constraint class. Conversion requires +an explicit `acknowledge_ignored_constraints=True`, and the omission receipt is +stored in every manifest and audit. A conflict-core SAT answer is not a complete +ITC timetable certificate. + +## Frozen event bank + +The bank contains 1,024 unique events in this order: + +1. the unchanged published baseline; +2. every single-period closure; +3. every single flexible-exam move to its alternative; +4. deterministic multi-exam move sets of width 2 through 12; +5. deterministic period-closure plus multi-move events. + +The seed is the first 64 bits of SHA-256 over the exact instance bytes, a NUL byte, +and the exact solution bytes. Widths, exam rankings and closed periods are then +derived directly from labelled SHA-256 words rather than Python's version-sensitive +sampling helpers. Duplicate exact queries are removed before the bank is +truncated. Failure to create 1,024 distinct events is a protocol failure, not +permission to alter the policy after observing results. + +## Engines and measured boundary + +The same immutable packet is run through `auto`, `raw`, `scc`, and `route`. Each +mode receives one untimed warmup; five measured repetitions rotate the mode order +so no mode always executes first or last. The measured boundary starts with ready +`SBC1` bytes and includes: + +- input validation and native construction; +- every query decision; +- complete SAT witnesses or portable UNSAT proofs; +- the separately owned native observer; +- `SCQ2` materialization; +- native disposal and the Python call boundary. + +External checkout, parsing, candidate generation, packet packing, native +compilation, evidence writes, and offline replay are outside the timing boundary. +Timing is descriptive for this single development transfer; there is no speed-win +gate here. The separate prospective multi-instance suite declares its holdout +threshold before execution. + +## Freeze, execution and audit + +`freeze` validates everything before atomically publishing an evidence directory. +It writes external bytes, the upstream license, candidate rows, canonical queries, +packet, source inventory, commit/tree identities and hashes with exclusive +creation. Every listed source blob must match the named commit. + +`run` explicitly builds the bundled ABI-3 runtime, records its receipt hash, runs +all engines/repetitions, requires byte-stable repeated output per engine, and +verifies every output before publication. + +`audit` is rerunnable without a native solver. It reparses the original instance +and solution, rechecks all baseline hard constraints, regenerates the seed, +candidate rows and complete event bank, reconstructs the packet, checks recorded +metadata against the certified bytes, replays every output, requires all modes to +agree on SAT/UNSAT decisions, counts proof kinds, and refuses changed evidence or +a changed prior audit. Stored candidate/query JSON is evidence, not an oracle. + +## Interpretation + +A successful run demonstrates that the exact API transfers from exposed synthetic +graph panels to a published timetabling-format repair core while retaining full +proof boundaries. It remains project-owned execution on a deterministic derived +problem. It is not operator adoption, outside-team replication, a claim of +algorithmic priority, or evidence that SPECTRA solves the full ITC-2007 task. diff --git a/experiments/compiled_query/exact.py b/experiments/compiled_query/exact.py index 780a7ff..7eee4c2 100644 --- a/experiments/compiled_query/exact.py +++ b/experiments/compiled_query/exact.py @@ -363,11 +363,13 @@ def solve_session_packet(self, packet: bytes, *, mode: str = 'route', max_bytes: int = DEFAULT_BYTES) -> ExactSessionPacketAnswer: """Construct, solve, verify, serialize and dispose one prepared SBC1 session. - Opt-in ``auto`` uses raw traversal below 64 requests or on a base-infeasible - graph. Otherwise it uses routed proofs when SCCs at least halve the - literal graph, and plain SCC batching when they do not. Selection uses - fixed-graph structure and bank length, never measured answers or timings. - ``info['selected_mode']`` names the actual engine. Explicit modes and the + Opt-in ``auto`` uses structural policy v2. It selects raw traversal below + 64 requests, on a base-infeasible graph, or when the immutable bank carries + at least eight pins per request on average. Otherwise it uses routed proofs + when SCCs at least halve the literal graph, and plain SCC batching when they + do not. Selection uses only admitted graph/request geometry, never answers + or measured timings. ``info['selected_mode']`` and + ``info['auto_policy_reason']`` expose the decision. Explicit modes and the default retain their meaning; this policy is an experimental heuristic. """ if type(packet) is not bytes: diff --git a/experiments/compiled_query/exact_native.cpp b/experiments/compiled_query/exact_native.cpp index 96182ee..96694d3 100644 --- a/experiments/compiled_query/exact_native.cpp +++ b/experiments/compiled_query/exact_native.cpp @@ -341,12 +341,13 @@ struct ObserverSessionPacket { struct Answer {bool sat=true;std::vector labels,certificate;W visits=0,arcs=0;}; struct Index { U n,k,nc=0,bad=NIL;bool condensed,sparse,routed;W cap;W full_arcs=0; + const char* auto_reason="explicit_mode"; std::vector masks; std::vector lo,hi,base; std::vector comp,opposite; std::vector inward,outward,representative,cross_from,cross_to; CSR original,dag; - Index(Input p,U mode,U bank_size=0):n(p.n),k(p.k),condensed(mode!=0),sparse(mode==2),routed(mode==3),cap(p.cap),masks(std::move(p.masks)),lo(n),hi(n,255),base(n) { + Index(Input p,U mode,U bank_size=0,U bank_pins=0):n(p.n),k(p.k),condensed(mode!=0),sparse(mode==2),routed(mode==3),cap(p.cap),masks(std::move(p.masks)),lo(n),hi(n,255),base(n) { std::vector> arcs;arcs.reserve(4*p.edges.size()+n); for(U v=0;v(comp[2*v]=64 && bad==NIL; - routed=condensed && nc<=n; + // Structural auto policy v2. set9 development evidence showed that + // large, heavily pinned repair banks can lose to raw traversal even + // when tile64 is available. The policy therefore uses only immutable + // request geometry (never answers or measured timings): raw below one + // tile, on a base contradiction, or at eight pins per row and above. + // The threshold is frozen before the set10/set12 holdout transfer. + if(bank_size<64) { + condensed=false;routed=false;auto_reason="bank_below_tile64"; + } else if(bad!=NIL) { + condensed=false;routed=false;auto_reason="base_infeasible"; + } else if(W(bank_pins)>=8*W(bank_size)) { + condensed=false;routed=false;auto_reason="high_pin_density"; + } else { + condensed=true;routed=nc<=n; + auto_reason=routed?"route_component_compression":"scc_tile64"; + } } if(condensed && bad==NIL) { opposite.assign(nc,NIL); @@ -990,6 +1002,7 @@ PyObject* solve_session_packet(PyObject*,PyObject* args) { const auto* data=reinterpret_cast(PyBytes_AS_STRING(packet)); size_t size=static_cast(PyBytes_GET_SIZE(packet)); std::unique_ptr solver;std::unique_ptr observer;BankResult result; + U bank_queries=0,bank_pins=0; { Released guard; ObserverSessionPacket observer_rows(data,size,limit); @@ -997,8 +1010,9 @@ PyObject* solve_session_packet(PyObject*,PyObject* args) { W observer_bytes=observer->bytes(); if(observer_bytes>=limit) throw Resource("SBC1 observer consumes session cap"); SolverSessionPacket solver_rows(data,size,limit-observer_bytes); + bank_queries=solver_rows.query_count();bank_pins=solver_rows.pin_count(); solver=std::make_unique(solver_rows.take_input(limit-observer_bytes), - static_cast(which),solver_rows.query_count()); + static_cast(which),bank_queries,bank_pins); result=run_rows(*solver,*observer,solver_rows,observer_rows,size,limit,"SBC1"); } PyObject* output=PyBytes_FromStringAndSize(reinterpret_cast(result.output.data()), @@ -1018,11 +1032,22 @@ PyObject* solve_session_packet(PyObject*,PyObject* args) { if(!metadata){Py_DECREF(output);return nullptr;} PyObject* kernel=PyUnicode_FromString(result.tiled?"tile64":"serial"); PyObject* tiles=PyLong_FromUnsignedLong(result.tiles); - if(!kernel || !tiles || PyDict_SetItemString(metadata,"bank_kernel",kernel)<0 - || PyDict_SetItemString(metadata,"bank_tiles",tiles)<0) { - Py_XDECREF(kernel);Py_XDECREF(tiles);Py_DECREF(output);Py_DECREF(metadata);return nullptr; + PyObject* queries=PyLong_FromUnsignedLong(bank_queries); + PyObject* pins=PyLong_FromUnsignedLong(bank_pins); + PyObject* policy=PyUnicode_FromString(which==4?"structural-v2":"explicit"); + PyObject* reason=PyUnicode_FromString(solver->auto_reason); + if(!kernel || !tiles || !queries || !pins || !policy || !reason + || PyDict_SetItemString(metadata,"bank_kernel",kernel)<0 + || PyDict_SetItemString(metadata,"bank_tiles",tiles)<0 + || PyDict_SetItemString(metadata,"bank_query_count",queries)<0 + || PyDict_SetItemString(metadata,"bank_pin_count",pins)<0 + || PyDict_SetItemString(metadata,"auto_policy",policy)<0 + || PyDict_SetItemString(metadata,"auto_policy_reason",reason)<0) { + Py_XDECREF(kernel);Py_XDECREF(tiles);Py_XDECREF(queries);Py_XDECREF(pins); + Py_XDECREF(policy);Py_XDECREF(reason);Py_DECREF(output);Py_DECREF(metadata);return nullptr; } - Py_DECREF(kernel);Py_DECREF(tiles); + Py_DECREF(kernel);Py_DECREF(tiles);Py_DECREF(queries);Py_DECREF(pins); + Py_DECREF(policy);Py_DECREF(reason); return Py_BuildValue("NKKKKKKN",output, static_cast(result.sat+result.unsat), static_cast(result.sat), diff --git a/experiments/compiled_query/itc_exam_suite.py b/experiments/compiled_query/itc_exam_suite.py new file mode 100644 index 0000000..dfc8b71 --- /dev/null +++ b/experiments/compiled_query/itc_exam_suite.py @@ -0,0 +1,352 @@ +"""Prospective multi-instance ITC-2007 exam-repair transfer suite. + +The selector-v2 rule is developed from the exposed synthetic panel and the +published ``set9`` transfer only. ``set10`` and ``set12`` are fixed holdout +instances: their execution results must not be used to retune the policy. This +suite remains a bounded conflict-core repair study, not a full timetabling claim. +""" +from __future__ import annotations + +import argparse +from dataclasses import dataclass +import json +from pathlib import Path, PurePosixPath +import shutil +import statistics +from typing import Iterable + +from experiments.compiled_query import itc_exam_transfer as transfer + +SCHEMA = "spectra.itc2007.exam_repair_suite.v1" +DEFAULT_HOLDOUT_MAX_RATIO = 1.15 + + +@dataclass(frozen=True) +class SuiteCase: + name: str + role: str + instance_path: str + solution_path: str + + def validate(self) -> "SuiteCase": + if (type(self.name) is not str or not self.name + or any(ch not in "abcdefghijklmnopqrstuvwxyz0123456789-" for ch in self.name) + or self.name.startswith("-") or self.name.endswith("-")): + raise ValueError("suite case name must be lowercase kebab-case") + if self.role not in ("development", "holdout", "replication"): + raise ValueError("suite case role differs") + for label, value in (("instance", self.instance_path), ("solution", self.solution_path)): + if type(value) is not str or not value: + raise ValueError(f"suite case {label} path must be nonempty") + path = PurePosixPath(value) + if path.is_absolute() or ".." in path.parts: + raise ValueError(f"suite case {label} path must stay under upstream root") + return self + + +DEFAULT_CASES = ( + SuiteCase( + "set9-development", "development", + "examtt-instances/Instances/set9.exam", + "InitMethods/SolStore/set9_OBSIFeas_0.sol", + ), + SuiteCase( + "set10-holdout", "holdout", + "examtt-instances/Instances/set10.exam", + "InitMethods/SolStore/set10_OBSIFeas_0.sol", + ), + SuiteCase( + "set12-holdout", "holdout", + "examtt-instances/Instances/set12.exam", + "InitMethods/SolStore/set12_OBSIFeas_0.sol", + ), +) + + +def _cases(cases: Iterable[SuiteCase]) -> tuple[SuiteCase, ...]: + checked = tuple(case.validate() for case in cases) + if not checked or len({case.name for case in checked}) != len(checked): + raise ValueError("suite requires unique nonempty cases") + if not any(case.role == "holdout" for case in checked): + raise ValueError("suite requires at least one holdout") + return checked + + +def _ratio(value: float | None) -> float | None: + if value is None: + return None + if type(value) not in (int, float) or not 1.0 <= float(value) <= 10.0: + raise ValueError("holdout max auto/raw ratio must be in [1, 10] or null") + return float(value) + + +def _manifest_path(root: Path) -> Path: + return root / "suite-manifest.json" + + +def freeze(root: Path, upstream_root: Path, *, cases: Iterable[SuiteCase] = DEFAULT_CASES, + query_count: int = 1024, + holdout_max_auto_raw_ratio: float | None = DEFAULT_HOLDOUT_MAX_RATIO, + source_commit: str | None = None) -> dict: + root = root.resolve() + upstream_root = upstream_root.resolve() + if root.exists(): + raise FileExistsError(root) + checked = _cases(cases) + threshold = _ratio(holdout_max_auto_raw_ratio) + license_path = upstream_root / "LICENSE" + if not license_path.is_file(): + raise FileNotFoundError(license_path) + staging = root.with_name(root.name + ".partial") + if staging.exists(): + raise FileExistsError(staging) + (staging / "cases").mkdir(parents=True, exist_ok=False) + try: + case_records = [] + for case in checked: + case_root = staging / "cases" / case.name + manifest = transfer.freeze( + case_root, + upstream_root / case.instance_path, + upstream_root / case.solution_path, + query_count=query_count, + license_path=license_path, + source_commit=source_commit, + dataset_instance_path=case.instance_path, + dataset_solution_path=case.solution_path, + evaluation_role=case.role, + ) + case_records.append({ + "name": case.name, + "role": case.role, + "instance_path": case.instance_path, + "solution_path": case.solution_path, + "manifest_sha256": (case_root / "manifest.sha256").read_text().strip(), + "instance_sha256": manifest["instance_sha256"], + "solution_sha256": manifest["solution_sha256"], + }) + manifest = { + "schema": SCHEMA, + "classification": ( + "prospective multi-instance bounded exam-repair transfer; set9 is " + "development and set10/set12 are fixed execution holdouts" + ), + "source_commit": case_records and json.loads( + (staging / "cases" / case_records[0]["name"] / "manifest.json").read_text() + )["source_commit"], + "dataset_repository": transfer.DATASET_REPOSITORY, + "dataset_commit": transfer.DATASET_COMMIT, + "query_count_per_case": query_count, + "selector_policy": "structural-v2", + "selector_development_sources": [ + "exposed synthetic graph panel", + "set9 development transfer", + ], + "holdout_policy": ( + "set10 and set12 results may falsify the frozen selector but may not " + "change it inside this suite" + ), + "holdout_max_auto_raw_ratio": threshold, + "gate_metric": "median of paired per-repetition auto/raw elapsed ratios", + "cases": case_records, + } + payload = transfer.canonical_json(manifest) + transfer.write_once(_manifest_path(staging), payload) + transfer.write_once( + staging / "suite-manifest.sha256", + (transfer.digest(payload) + "\n").encode(), + ) + staging.rename(root) + return manifest + except BaseException: + shutil.rmtree(staging, ignore_errors=True) + raise + + +def load_manifest(root: Path) -> dict: + root = root.resolve() + expected = (root / "suite-manifest.sha256").read_text().strip() + manifest = json.loads(transfer.read_bound(_manifest_path(root), expected)) + if (manifest.get("schema") != SCHEMA + or manifest.get("dataset_repository") != transfer.DATASET_REPOSITORY + or manifest.get("dataset_commit") != transfer.DATASET_COMMIT + or manifest.get("selector_policy") != "structural-v2" + or type(manifest.get("query_count_per_case")) is not int + or manifest["query_count_per_case"] < 1): + raise ValueError("suite manifest identity differs") + threshold = _ratio(manifest.get("holdout_max_auto_raw_ratio")) + records = manifest.get("cases") + if type(records) is not list: + raise ValueError("suite case inventory differs") + checked = _cases(SuiteCase( + item.get("name"), item.get("role"), + item.get("instance_path"), item.get("solution_path"), + ) for item in records if type(item) is dict) + if len(checked) != len(records): + raise ValueError("suite case inventory differs") + for case, item in zip(checked, records): + case_root = root / "cases" / case.name + if ((case_root / "manifest.sha256").read_text().strip() + != item.get("manifest_sha256")): + raise ValueError(f"suite case {case.name} manifest hash differs") + child = transfer.load_manifest(case_root) + if (child.get("dataset_instance_path") != case.instance_path + or child.get("dataset_solution_path") != case.solution_path + or child.get("evaluation_role") != case.role + or child.get("query_count") != manifest["query_count_per_case"] + or child.get("instance_sha256") != item.get("instance_sha256") + or child.get("solution_sha256") != item.get("solution_sha256")): + raise ValueError(f"suite case {case.name} identity differs") + manifest["holdout_max_auto_raw_ratio"] = threshold + return manifest + + +def run(root: Path) -> dict: + root = root.resolve() + manifest = load_manifest(root) + if (root / "suite-run.json").exists(): + raise FileExistsError("suite execution already exists") + records = [] + for item in manifest["cases"]: + case_root = root / "cases" / item["name"] + record = transfer.run(case_root) + records.append({ + "name": item["name"], + "run_sha256": (case_root / "run.sha256").read_text().strip(), + "execution_order": record["execution_order"], + }) + result = { + "schema": SCHEMA + ".run", + "suite_manifest_sha256": (root / "suite-manifest.sha256").read_text().strip(), + "cases": records, + } + payload = transfer.canonical_json(result) + transfer.write_once(root / "suite-run.json", payload) + transfer.write_once(root / "suite-run.sha256", (transfer.digest(payload) + "\n").encode()) + return result + + +def audit(root: Path) -> dict: + root = root.resolve() + manifest = load_manifest(root) + run_expected = (root / "suite-run.sha256").read_text().strip() + run_record = json.loads(transfer.read_bound(root / "suite-run.json", run_expected)) + if (run_record.get("schema") != SCHEMA + ".run" + or run_record.get("suite_manifest_sha256") + != (root / "suite-manifest.sha256").read_text().strip()): + raise ValueError("suite run identity differs") + run_cases = run_record.get("cases") + if type(run_cases) is not list or len(run_cases) != len(manifest["cases"]): + raise ValueError("suite run case inventory differs") + + threshold = manifest["holdout_max_auto_raw_ratio"] + summaries = [] + holdout_failures = [] + for item, run_item in zip(manifest["cases"], run_cases): + if type(run_item) is not dict or run_item.get("name") != item["name"]: + raise ValueError("suite run case order differs") + case_root = root / "cases" / item["name"] + if (case_root / "run.sha256").read_text().strip() != run_item.get("run_sha256"): + raise ValueError(f"suite case {item['name']} run hash differs") + result = transfer.audit(case_root) + child_run = json.loads(transfer.read_bound( + case_root / "run.json", (case_root / "run.sha256").read_text().strip() + )) + auto_elapsed = child_run["modes"]["auto"]["elapsed_ns"] + raw_elapsed = child_run["modes"]["raw"]["elapsed_ns"] + paired_median = statistics.median( + auto / raw for auto, raw in zip(auto_elapsed, raw_elapsed) + ) + selected = result["modes"]["auto"]["selected_mode"] + reason = result["modes"]["auto"]["auto_policy_reason"] + gate_pass = True + if item["role"] == "holdout" and threshold is not None: + gate_pass = paired_median <= threshold + if not gate_pass: + holdout_failures.append(item["name"]) + summaries.append({ + "name": item["name"], + "role": item["role"], + "functional_complete": result["functional_complete"], + "baseline_valid_full_itc_hard_constraints": result[ + "baseline_valid_full_itc_hard_constraints" + ], + "instance_sha256": result["instance_sha256"], + "solution_sha256": result["solution_sha256"], + "model": result["model"], + "queries": result["queries"], + "sat": result["modes"]["auto"]["sat"], + "unsat": result["modes"]["auto"]["unsat"], + "proof_kinds": result["modes"]["auto"]["proof_kinds"], + "auto_selected_mode": selected, + "auto_policy_reason": reason, + "auto_vs_raw_median_ratio": result["auto_vs_raw_median_ratio"], + "auto_vs_raw_paired_median_ratio": paired_median, + "holdout_gate_pass": gate_pass, + "audit_sha256": (case_root / "audit.sha256").read_text().strip(), + }) + functional = all( + item["functional_complete"] and item["baseline_valid_full_itc_hard_constraints"] + for item in summaries + ) + result = { + "schema": SCHEMA + ".audit", + "functional_complete": functional, + "source_commit": manifest["source_commit"], + "dataset_commit": manifest["dataset_commit"], + "selector_policy": manifest["selector_policy"], + "query_count_per_case": manifest["query_count_per_case"], + "holdout_max_auto_raw_ratio": threshold, + "holdout_gate_pass": not holdout_failures, + "holdout_failures": holdout_failures, + "cases": summaries, + "interpretation": [ + "set9 is development evidence; set10 and set12 are prospective execution holdouts", + "the gate limits auto-policy overhead relative to the explicit raw control", + "all outputs remain complete proof-carrying conflict-core repair answers", + "this is not full ITC-2007 timetable feasibility or optimization", + "project-owned CI is not outside-team replication or adoption", + ], + } + payload = transfer.canonical_json(result) + path = root / "suite-audit.json" + if path.exists(): + expected = (root / "suite-audit.sha256").read_text().strip() + if transfer.read_bound(path, expected) != payload: + raise ValueError("existing suite audit differs from independent replay") + else: + transfer.write_once(path, payload) + transfer.write_once( + root / "suite-audit.sha256", (transfer.digest(payload) + "\n").encode() + ) + return result + + +def main(argv: Iterable[str] | None = None) -> int: + parser = argparse.ArgumentParser() + parser.add_argument("command", choices=("freeze", "run", "audit")) + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--upstream-root", type=Path) + parser.add_argument("--queries", type=int, default=1024) + parser.add_argument( + "--holdout-max-auto-raw-ratio", type=float, + default=DEFAULT_HOLDOUT_MAX_RATIO, + ) + args = parser.parse_args(argv) + if args.command == "freeze": + if args.upstream_root is None: + parser.error("freeze requires --upstream-root") + value = freeze( + args.root, args.upstream_root, query_count=args.queries, + holdout_max_auto_raw_ratio=args.holdout_max_auto_raw_ratio, + ) + elif args.command == "run": + value = run(args.root) + else: + value = audit(args.root) + print(json.dumps(value, indent=2, sort_keys=True, allow_nan=False)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/experiments/compiled_query/itc_exam_transfer.py b/experiments/compiled_query/itc_exam_transfer.py new file mode 100644 index 0000000..fbbc025 --- /dev/null +++ b/experiments/compiled_query/itc_exam_transfer.py @@ -0,0 +1,752 @@ +"""Proof-carrying bounded repair on a pinned ITC-2007 examination instance. + +This is a transfer study for the installed ``spectra.exact`` API, not a claim to +solve the complete capacitated timetabling objective. It starts from a published +feasible timetable, freezes exams involved in unsupported hard constraints, gives +other exams one deterministic duration-compatible alternative period, and solves +only the shared-student/EXCLUSION conflict core. Every result is replayed from the +stored SBC1 packet and SCQ2 evidence without trusting the native index. +""" +from __future__ import annotations + +import argparse +from collections import Counter, defaultdict +import hashlib +import json +from pathlib import Path +import shutil +import statistics +import subprocess +import sys +import time +from typing import Iterable + +from spectra.exact import ( + ExactRuntime, + build_exact_runtime, + decode_certified_output, + pack_validated_session_packet, + verify_session_output, +) +from spectra.exact_proof import decode_proof +from spectra.itc2007_exam import ITCExamInstance, parse_itc2007_exam + +SCHEMA = "spectra.itc2007.exam_repair_transfer.v1" +DATASET_REPOSITORY = "davidvanbulck/ITC-2007-CETT" +DATASET_COMMIT = "ed2d4c0cf5302b77a8b321cd0380de0bb579d538" +DEFAULT_INSTANCE = "examtt-instances/Instances/set9.exam" +DEFAULT_SOLUTION = "InitMethods/SolStore/set9_OBSIFeas_0.sol" +MODES = ("auto", "raw", "scc", "route") +WARMUPS = 1 +REPEATS = 5 +MAX_INPUT_BYTES = 64 << 20 +SOURCE_PATHS = ( + Path("pyproject.toml"), Path("README.md"), + Path("spectra/exact.py"), Path("spectra/exact_problem.py"), + Path("spectra/exact_proof.py"), Path("spectra/exam_repair.py"), + Path("spectra/itc2007_exam.py"), + Path("spectra/_native/exact_binary.cpp"), + Path("tests/public/test_exam_repair.py"), + Path("tests/public/test_itc2007_exam.py"), + Path("scripts/check_exam_repair_installation.py"), + Path("docs/EXAM_REPAIR.md"), Path("docs/README.md"), + Path("experiments/compiled_query/exact.py"), + Path("experiments/compiled_query/exact_native.cpp"), + Path("experiments/compiled_query/test_exact.py"), + Path("experiments/compiled_query/itc_exam_transfer.py"), + Path("experiments/compiled_query/itc_exam_suite.py"), + Path("experiments/compiled_query/test_itc_exam_transfer.py"), + Path("experiments/compiled_query/test_itc_exam_suite.py"), + Path("experiments/compiled_query/ITC_EXAM_TRANSFER_PROTOCOL.md"), + Path("experiments/compiled_query/ITC_EXAM_SUITE_PROTOCOL.md"), + Path(".github/workflows/itc-exam-transfer.yml"), + Path(".github/workflows/itc-exam-suite.yml"), +) + +def digest(data: bytes) -> str: + return hashlib.sha256(data).hexdigest() + + +def canonical_json(value: object) -> bytes: + return (json.dumps(value, sort_keys=True, separators=(",", ":"), + ensure_ascii=True, allow_nan=False) + "\n").encode() + + +def write_once(path: Path, data: bytes) -> str: + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("xb") as stream: + stream.write(data) + return digest(data) + + +def deterministic_word(seed: int, *parts: int) -> int: + payload = seed.to_bytes(8, "little", signed=False) + for part in parts: + if type(part) is not int or part < 0: + raise ValueError("deterministic schedule parts must be nonnegative exact integers") + payload += part.to_bytes(8, "little", signed=False) + return int.from_bytes(hashlib.sha256(payload).digest()[:8], "little") + + +def read_bound(path: Path, expected: str | None = None, + *, max_bytes: int = MAX_INPUT_BYTES) -> bytes: + with path.open("rb") as stream: + data = stream.read(max_bytes + 1) + if len(data) > max_bytes: + raise ValueError(f"{path} exceeds the bounded read limit") + if expected is not None and digest(data) != expected: + raise ValueError(f"{path} hash differs") + return data + + +def parse_solution(text: str, instance: ITCExamInstance) -> tuple[tuple[int, ...], tuple[int, ...]]: + if type(text) is not str: + raise ValueError("solution must be an exact string") + periods: list[int] = [] + rooms: list[int] = [] + for line_number, raw in enumerate(text.replace("\r\n", "\n").replace("\r", "\n").split("\n"), 1): + line = raw.strip() + if not line: + continue + fields = tuple(value.strip() for value in line.split(",")) + if len(fields) != 2 or any(not value.isascii() or not value.isdigit() for value in fields): + raise ValueError(f"line {line_number}: solution row must be period,room") + period, room = map(int, fields) + if period >= len(instance.periods) or room >= len(instance.rooms): + raise ValueError(f"line {line_number}: solution index outside instance") + periods.append(period) + rooms.append(room) + if len(periods) != len(instance.exams): + raise ValueError("solution row count differs from exam count") + return tuple(periods), tuple(rooms) + + +def check_full_baseline(instance: ITCExamInstance, periods: tuple[int, ...], + rooms: tuple[int, ...]) -> None: + """Check the published timetable's hard constraints before deriving repairs.""" + if len(periods) != len(instance.exams) or len(rooms) != len(instance.exams): + raise ValueError("baseline geometry differs") + roster: dict[int, list[int]] = defaultdict(list) + for exam_index, exam in enumerate(instance.exams): + period = periods[exam_index] + room = rooms[exam_index] + if instance.periods[period].duration < exam.duration: + raise ValueError(f"baseline exam {exam_index} exceeds period duration") + for student in exam.students: + roster[student].append(exam_index) + if not 0 <= room < len(instance.rooms): + raise ValueError("baseline room outside bank") + for student, exams in roster.items(): + seen: set[int] = set() + for exam in exams: + if periods[exam] in seen: + raise ValueError(f"baseline repeats student {student} in one period") + seen.add(periods[exam]) + for left, right in instance.exclusions: + if periods[left] == periods[right]: + raise ValueError("baseline violates an EXCLUSION constraint") + for left, right in instance.coincidences: + if periods[left] != periods[right]: + raise ValueError("baseline violates an EXAM_COINCIDENCE constraint") + for later, earlier in instance.after: + if periods[later] <= periods[earlier]: + raise ValueError("baseline violates an AFTER constraint") + usage: dict[tuple[int, int], int] = defaultdict(int) + occupants: dict[tuple[int, int], list[int]] = defaultdict(list) + for exam_index, exam in enumerate(instance.exams): + key = (periods[exam_index], rooms[exam_index]) + usage[key] += len(exam.students) + occupants[key].append(exam_index) + for (period, room), count in usage.items(): + if count > instance.rooms[room].capacity: + raise ValueError(f"baseline room capacity exceeded at period {period}, room {room}") + for exam in instance.room_exclusive: + if len(occupants[periods[exam], rooms[exam]]) != 1: + raise ValueError("baseline violates ROOM_EXCLUSIVE") + + +def protected_exams(instance: ITCExamInstance) -> set[int]: + protected = set(instance.room_exclusive) + for left, right in (*instance.after, *instance.coincidences): + protected.add(left) + protected.add(right) + return protected + + +def derive_candidates(instance: ITCExamInstance, baseline_periods: tuple[int, ...], + *, seed: int) -> tuple[tuple[tuple[int, ...], ...], tuple[int, ...]]: + """Choose one deterministic alternative while retaining the baseline witness.""" + protected = protected_exams(instance) + rows: list[tuple[int, ...]] = [] + flexible: list[int] = [] + max_room = max((room.capacity for room in instance.rooms), default=0) + for exam_index, exam in enumerate(instance.exams): + base = baseline_periods[exam_index] + if exam_index in protected: + rows.append((base,)) + continue + alternatives = [ + period for period, record in enumerate(instance.periods) + if period != base and record.duration >= exam.duration + and len(exam.students) <= max_room + ] + if not alternatives: + rows.append((base,)) + continue + # Hash-shuffled choice avoids concentrating all repairs in the earliest + # periods while remaining fixed by the frozen source/solution identity. + alternatives.sort(key=lambda period: hashlib.sha256( + f"{seed}/{exam_index}/{period}".encode("ascii")).digest()) + alternate = alternatives[0] + rows.append((base, alternate)) + flexible.append(exam_index) + return tuple(rows), tuple(flexible) + + +def generate_queries(core, baseline_periods: tuple[int, ...], flexible: tuple[int, ...], + *, target: int, seed: int) -> tuple[tuple[tuple[int, int], ...], ...]: + if type(target) is not int or not 1 <= target <= 100_000: + raise ValueError("query target outside [1, 100000]") + model = core.model + names = model.period_ids + exam_names = model.exam_ids + alternatives: dict[int, str] = {} + for exam in flexible: + base_name = names[baseline_periods[exam]] + alternatives[exam] = next(name for name in model.candidates[exam] if name != base_name) + queries: list[tuple[tuple[int, int], ...]] = [] + seen: set[tuple[tuple[int, int], ...]] = set() + + def add(query: tuple[tuple[int, int], ...]) -> None: + if query not in seen: + seen.add(query) + queries.append(query) + + add(()) + for period in names: + add(model.freeze_event(closed_periods=(period,))) + for exam in flexible: + add(model.freeze_event(fixed=((exam_names[exam], alternatives[exam]),))) + attempts = 0 + while len(queries) < target and attempts < target * 200: + attempts += 1 + if not flexible: + break + maximum = min(12, len(flexible)) + width = (2 + deterministic_word(seed, attempts, 0) % (maximum - 1) + if maximum >= 2 else 1) + ranked = sorted( + flexible, + key=lambda exam: (deterministic_word(seed, attempts, 1, exam), exam), + ) + chosen = tuple(sorted(ranked[:width])) + fixed = tuple((exam_names[exam], alternatives[exam]) for exam in chosen) + if attempts % 3 == 0: + closed = (names[deterministic_word(seed, attempts, 2) % len(names)],) + query = model.freeze_event(closed_periods=closed, fixed=fixed) + else: + query = model.freeze_event(fixed=fixed) + add(query) + if len(queries) < target: + raise ValueError(f"could create only {len(queries)} distinct repair events") + return tuple(queries[:target]) + + +def _git(*args: str) -> str: + return subprocess.check_output(("git", *args), text=True).strip() + + +def source_inventory() -> dict[str, str]: + root = Path(__file__).resolve().parents[2] + return {str(path): digest((root / path).read_bytes()) for path in SOURCE_PATHS} + + +def validate_source_commit(commit: str, inventory: dict[str, str]) -> str: + if (type(commit) is not str or len(commit) != 40 + or any(character not in "0123456789abcdef" for character in commit)): + raise ValueError("source_commit must be a full lowercase commit SHA") + resolved = _git("rev-parse", "--verify", commit + "^{commit}") + if resolved != commit: + raise ValueError("source_commit must identify the commit itself") + for path, expected in inventory.items(): + try: + data = subprocess.check_output(("git", "show", f"{commit}:{path}")) + except subprocess.CalledProcessError as error: + raise ValueError(f"source commit does not contain {path}") from error + if digest(data) != expected: + raise ValueError(f"working source differs from source_commit at {path}") + return _git("rev-parse", commit + "^{tree}") + + +def freeze(root: Path, instance_path: Path, solution_path: Path, *, query_count: int, + license_path: Path | None = None, source_commit: str | None = None, + dataset_instance_path: str = DEFAULT_INSTANCE, + dataset_solution_path: str = DEFAULT_SOLUTION, + evaluation_role: str = "development") -> dict: + root = root.resolve() + if root.exists(): + raise FileExistsError(root) + if license_path is None: + raise ValueError("freeze requires the upstream redistribution license") + if (type(dataset_instance_path) is not str or not dataset_instance_path + or type(dataset_solution_path) is not str or not dataset_solution_path): + raise ValueError("dataset paths must be nonempty exact strings") + if evaluation_role not in ("development", "holdout", "replication"): + raise ValueError("evaluation_role must be development, holdout or replication") + instance_bytes = read_bound(instance_path) + solution_bytes = read_bound(solution_path) + license_bytes = read_bound(license_path) + instance = parse_itc2007_exam(instance_bytes.decode("utf-8")) + baseline_periods, baseline_rooms = parse_solution(solution_bytes.decode("utf-8"), instance) + check_full_baseline(instance, baseline_periods, baseline_rooms) + seed = int.from_bytes(hashlib.sha256(instance_bytes + b"\0" + solution_bytes).digest()[:8], "little") + if len(instance.periods) > 64: + raise ValueError("selected ITC instance exceeds the exact API's 64-period palette") + candidates, flexible = derive_candidates(instance, baseline_periods, seed=seed) + core = instance.conflict_core(candidates, acknowledge_ignored_constraints=True) + if not core.model.problem.check(bytes(baseline_periods)): + raise ValueError("published baseline does not witness the derived conflict core") + queries = generate_queries(core, baseline_periods, flexible, target=query_count, seed=seed) + packet = pack_validated_session_packet(core.model.problem, queries) + inventory = source_inventory() + commit = source_commit or _git("rev-parse", "HEAD") + source_tree: str | None = None + if source_commit is None: + changed = _git("status", "--porcelain", "--", *(str(path) for path in SOURCE_PATHS)) + if changed: + raise ValueError("commit the transfer sources before freezing evidence") + source_tree = validate_source_commit(commit, inventory) + elif type(source_commit) is not str or not source_commit: + raise ValueError("source_commit fixture identity must be a nonempty string") + + state = { + "baseline_periods": baseline_periods, + "baseline_rooms": baseline_rooms, + "candidate_periods": candidates, + "flexible_exams": flexible, + "protected_exams": tuple(sorted(protected_exams(instance))), + "seed": seed, + } + staging = root.with_name(root.name + ".partial") + if staging.exists(): + raise FileExistsError(staging) + staging.mkdir(parents=True, exist_ok=False) + try: + artifacts = { + "inputs/instance.exam": write_once(staging / "inputs/instance.exam", instance_bytes), + "inputs/baseline.sol": write_once(staging / "inputs/baseline.sol", solution_bytes), + "inputs/UPSTREAM_LICENSE.txt": write_once( + staging / "inputs/UPSTREAM_LICENSE.txt", license_bytes + ), + "candidates.json": write_once(staging / "candidates.json", canonical_json(state)), + "queries.json": write_once(staging / "queries.json", canonical_json(queries)), + "session.sbc1": write_once(staging / "session.sbc1", packet), + } + manifest = { + "schema": SCHEMA, + "classification": ( + "published ITC-2007 input and feasible schedule; deterministic bounded " + "conflict-core transfer, not full timetabling or outside-team replication" + ), + "source_commit": commit, + "source_tree": source_tree, + "source_inventory": inventory, + "dataset_repository": DATASET_REPOSITORY, + "dataset_commit": DATASET_COMMIT, + "dataset_instance_path": dataset_instance_path, + "dataset_solution_path": dataset_solution_path, + "evaluation_role": evaluation_role, + "instance_sha256": digest(instance_bytes), + "solution_sha256": digest(solution_bytes), + "upstream_acknowledgment": ( + "This product includes software/data distributed by David Van Bulck, " + "Dries Goossens, and Andrea Schaerf, Ghent University, Belgium " + "(https://github.com/davidvanbulck/ITC-2007-CETT.git)." + ), + "upstream_license_sha256": digest(license_bytes), + "model": core.info, + "candidate_policy": ( + "published baseline; freeze AFTER/coincidence/room-exclusive exams; " + "one hash-selected duration/individual-room-compatible alternative otherwise" + ), + "query_policy": ( + "base, every single-period closure, every single-exam move, then seeded " + "multi-move and closure-plus-move events" + ), + "query_count": len(queries), + "modes": MODES, + "warmups": WARMUPS, + "repeats": REPEATS, + "timing_schedule": "one warmup per mode; measured modes rotate by repetition", + "timing_scope": ( + "ready immutable SBC1 through construction, decisions, full evidence, " + "independent native check, SCQ2 materialization and disposal" + ), + "artifacts": artifacts, + } + manifest_bytes = canonical_json(manifest) + write_once(staging / "manifest.json", manifest_bytes) + write_once(staging / "manifest.sha256", (digest(manifest_bytes) + "\n").encode()) + staging.rename(root) + return manifest + except BaseException: + shutil.rmtree(staging, ignore_errors=True) + raise + + +def load_manifest(root: Path) -> dict: + expected = (root / "manifest.sha256").read_text().strip() + manifest_bytes = read_bound(root / "manifest.json", expected) + manifest = json.loads(manifest_bytes) + if (manifest.get("schema") != SCHEMA or tuple(manifest.get("modes", ())) != MODES + or type(manifest.get("warmups")) is not int or manifest["warmups"] < 0 + or type(manifest.get("repeats")) is not int or manifest["repeats"] < 1 + or type(manifest.get("query_count")) is not int or manifest["query_count"] < 1 + or manifest.get("timing_schedule") + != "one warmup per mode; measured modes rotate by repetition"): + raise ValueError("transfer manifest schema, modes or schedule differ") + if (manifest.get("dataset_repository") != DATASET_REPOSITORY + or manifest.get("dataset_commit") != DATASET_COMMIT + or type(manifest.get("dataset_instance_path")) is not str + or not manifest["dataset_instance_path"] + or type(manifest.get("dataset_solution_path")) is not str + or not manifest["dataset_solution_path"] + or manifest.get("evaluation_role") not in + ("development", "holdout", "replication")): + raise ValueError("transfer dataset identity differs") + inventory = manifest.get("source_inventory") + if type(inventory) is not dict or inventory != source_inventory(): + raise ValueError("transfer source inventory differs") + if manifest.get("source_tree") is not None: + if validate_source_commit(manifest.get("source_commit"), inventory) != manifest["source_tree"]: + raise ValueError("transfer source tree differs") + required = {"inputs/instance.exam", "inputs/baseline.sol", "candidates.json", + "queries.json", "session.sbc1", "inputs/UPSTREAM_LICENSE.txt"} + inventory = set(manifest.get("artifacts", ())) + if inventory != required: + raise ValueError("transfer manifest artifact inventory differs") + for name, checksum in manifest["artifacts"].items(): + read_bound(root / name, checksum) + if manifest["artifacts"]["inputs/instance.exam"] != manifest.get("instance_sha256"): + raise ValueError("manifest instance identity differs") + if manifest["artifacts"]["inputs/baseline.sol"] != manifest.get("solution_sha256"): + raise ValueError("manifest solution identity differs") + if (manifest["artifacts"]["inputs/UPSTREAM_LICENSE.txt"] + != manifest.get("upstream_license_sha256")): + raise ValueError("manifest upstream-license identity differs") + return manifest + + +def reconstruct(root: Path, manifest: dict): + instance_bytes = read_bound( + root / "inputs/instance.exam", manifest["artifacts"]["inputs/instance.exam"] + ) + solution_bytes = read_bound( + root / "inputs/baseline.sol", manifest["artifacts"]["inputs/baseline.sol"] + ) + instance = parse_itc2007_exam(instance_bytes.decode("utf-8")) + baseline_periods, baseline_rooms = parse_solution(solution_bytes.decode("utf-8"), instance) + check_full_baseline(instance, baseline_periods, baseline_rooms) + seed = int.from_bytes( + hashlib.sha256(instance_bytes + b"\0" + solution_bytes).digest()[:8], "little" + ) + candidates, flexible = derive_candidates(instance, baseline_periods, seed=seed) + expected_state = { + "baseline_periods": baseline_periods, + "baseline_rooms": baseline_rooms, + "candidate_periods": candidates, + "flexible_exams": flexible, + "protected_exams": tuple(sorted(protected_exams(instance))), + "seed": seed, + } + state = json.loads(read_bound( + root / "candidates.json", manifest["artifacts"]["candidates.json"] + )) + if canonical_json(state) != canonical_json(expected_state): + raise ValueError("stored candidate policy differs from original inputs") + core = instance.conflict_core(candidates, acknowledge_ignored_constraints=True) + if core.info != manifest.get("model"): + raise ValueError("stored model receipt differs from reconstructed inputs") + queries = tuple(tuple(tuple(pin) for pin in row) for row in json.loads(read_bound( + root / "queries.json", manifest["artifacts"]["queries.json"] + ))) + expected_queries = generate_queries( + core, baseline_periods, flexible, + target=manifest["query_count"], seed=seed, + ) + if queries != expected_queries: + raise ValueError("stored query bank differs from deterministic policy") + packet = read_bound(root / "session.sbc1", manifest["artifacts"]["session.sbc1"]) + if pack_validated_session_packet(core.model.problem, queries) != packet: + raise ValueError("stored packet differs from reconstructed original inputs") + if not core.model.problem.check(bytes(baseline_periods)): + raise ValueError("stored baseline no longer witnesses the reconstructed core") + return instance, state, core, queries + + +def run(root: Path) -> dict: + root = root.resolve() + manifest = load_manifest(root) + reconstruct(root, manifest) + if (root / "run.json").exists() or (root / "outputs").exists() or (root / "native-build").exists(): + raise FileExistsError("transfer execution already exists") + packet = read_bound(root / "session.sbc1", manifest["artifacts"]["session.sbc1"]) + build = root / "native-build" + library = build_exact_runtime(build) + runtime = ExactRuntime(library) + states: dict[str, dict[str, object]] = { + mode: {"elapsed_ns": [], "output": None, "metadata": None} for mode in MODES + } + execution_order: list[list[str]] = [] + + def execute(mode: str, *, measured: bool) -> None: + start = time.perf_counter_ns() + result = runtime.solve_session_packet(packet, mode=mode) + elapsed = time.perf_counter_ns() - start + if not verify_session_output(packet, result.output): + raise AssertionError(f"{mode} output failed standard-library replay") + current = { + "query_count": result.query_count, + "sat_count": result.sat_count, + "unsat_count": result.unsat_count, + "proof_bytes": result.proof_bytes, + "visited": result.visited, + "arcs_scanned": result.arcs_scanned, + "selected_mode": result.info.get("selected_mode", mode), + "bank_query_count": result.info.get("bank_query_count"), + "bank_pin_count": result.info.get("bank_pin_count"), + "bank_kernel": result.info.get("bank_kernel"), + "bank_tiles": result.info.get("bank_tiles"), + "auto_policy": result.info.get("auto_policy"), + "auto_policy_reason": result.info.get("auto_policy_reason"), + } + if (current["query_count"] != manifest["query_count"] + or current["sat_count"] + current["unsat_count"] != manifest["query_count"] + or current["bank_query_count"] != manifest["query_count"] + or any(type(current[name]) is not int or current[name] < 0 + for name in ("proof_bytes", "visited", "arcs_scanned", + "bank_pin_count", "bank_tiles")) + or current["selected_mode"] not in ("raw", "scc", "route") + or current["bank_kernel"] not in ("serial", "tile64") + or current["auto_policy"] not in ("explicit", "structural-v2") + or type(current["auto_policy_reason"]) is not str + or (mode == "auto" and current["auto_policy"] != "structural-v2") + or (mode != "auto" and (current["selected_mode"] != mode + or current["auto_policy"] != "explicit"))): + raise AssertionError(f"{mode} returned inconsistent metadata") + state = states[mode] + if state["output"] is None: + state["output"], state["metadata"] = result.output, current + elif state["output"] != result.output or state["metadata"] != current: + raise AssertionError(f"{mode} result changed across identical calls") + if measured: + elapsed_rows = state["elapsed_ns"] + assert isinstance(elapsed_rows, list) + elapsed_rows.append(elapsed) + + for _ in range(manifest["warmups"]): + for mode in MODES: + execute(mode, measured=False) + for repeat in range(manifest["repeats"]): + offset = repeat % len(MODES) + order = (*MODES[offset:], *MODES[:offset]) + execution_order.append(list(order)) + for mode in order: + execute(mode, measured=True) + + records: dict[str, object] = { + "schema": SCHEMA + ".run", + "manifest_sha256": (root / "manifest.sha256").read_text().strip(), + "build_receipt_sha256": digest((build / "build.json").read_bytes()), + "warmups": manifest["warmups"], + "execution_order": execution_order, + "modes": {}, + } + for mode in MODES: + state = states[mode] + output = state["output"] + metadata = state["metadata"] + elapsed = state["elapsed_ns"] + assert isinstance(output, bytes) and isinstance(metadata, dict) and isinstance(elapsed, list) + if len(elapsed) != manifest["repeats"]: + raise AssertionError(f"{mode} timing count differs") + output_name = f"outputs/{mode}.scq2" + output_hash = write_once(root / output_name, output) + records["modes"][mode] = { + **metadata, + "elapsed_ns": elapsed, + "output": output_name, + "output_sha256": output_hash, + } + run_bytes = canonical_json(records) + write_once(root / "run.json", run_bytes) + write_once(root / "run.sha256", (digest(run_bytes) + "\n").encode()) + return records + + +def audit(root: Path) -> dict: + root = root.resolve() + manifest = load_manifest(root) + run_hash = (root / "run.sha256").read_text().strip() + record = json.loads(read_bound(root / "run.json", run_hash)) + if record.get("schema") != SCHEMA + ".run" or set(record.get("modes", ())) != set(MODES): + raise ValueError("run schema or mode inventory differs") + if record.get("manifest_sha256") != (root / "manifest.sha256").read_text().strip(): + raise ValueError("run belongs to a different frozen manifest") + expected_order = [] + for repeat in range(manifest["repeats"]): + offset = repeat % len(MODES) + expected_order.append(list((*MODES[offset:], *MODES[:offset]))) + if (record.get("warmups") != manifest.get("warmups") + or record.get("execution_order") != expected_order): + raise ValueError("run warmup or rotated timing schedule differs") + packet = read_bound(root / "session.sbc1", manifest["artifacts"]["session.sbc1"]) + instance, state, core, queries = reconstruct(root, manifest) + receipt = read_bound(root / "native-build/build.json") + if digest(receipt) != record.get("build_receipt_sha256"): + raise ValueError("native build receipt hash differs") + decisions: tuple[int, ...] | None = None + summaries = {} + sample = None + for mode in MODES: + item = record["modes"][mode] + if type(item) is not dict or item.get("output") != f"outputs/{mode}.scq2": + raise ValueError(f"{mode} output location differs") + elapsed = item.get("elapsed_ns") + if (type(elapsed) is not list or len(elapsed) != manifest["repeats"] + or any(type(value) is not int or value <= 0 for value in elapsed)): + raise ValueError(f"{mode} timing schedule differs") + output = read_bound(root / item["output"], item["output_sha256"]) + if not verify_session_output(packet, output): + raise ValueError(f"{mode} output fails replay") + rows = decode_certified_output(output, core.model.problem.n, len(queries)) + current = tuple(code for code, _, _ in rows) + sat_count = sum(current) + unsat_count = len(current) - sat_count + proof_bytes = sum(len(proof) for code, _labels, proof in rows if not code) + selected = item.get("selected_mode") + policy = item.get("auto_policy") + reason = item.get("auto_policy_reason") + if (item.get("query_count") != len(queries) + or item.get("bank_query_count") != len(queries) + or item.get("sat_count") != sat_count + or item.get("unsat_count") != unsat_count + or item.get("proof_bytes") != proof_bytes + or any(type(item.get(name)) is not int or item[name] < 0 + for name in ("visited", "arcs_scanned", "bank_pin_count", "bank_tiles")) + or item.get("bank_kernel") not in ("serial", "tile64") + or selected not in ("raw", "scc", "route") + or policy not in ("explicit", "structural-v2") + or type(reason) is not str + or (mode == "auto" and policy != "structural-v2") + or (mode != "auto" and (selected != mode or policy != "explicit"))): + raise ValueError(f"{mode} metadata differs from certified output") + if decisions is None: + decisions = current + elif decisions != current: + raise ValueError("engine modes disagree on SAT/UNSAT decisions") + kinds = Counter() + for index, (code, _labels, proof) in enumerate(rows): + if not code: + kind, _, _ = decode_proof(proof, core.model.problem.n) + kinds[str(kind)] += 1 + if sample is None: + sample = { + "query_index": index, + "mode": mode, + "explanation": core.model.explain_unsat(queries[index], proof), + } + summaries[mode] = { + "median_elapsed_ns": statistics.median(elapsed), + "minimum_elapsed_ns": min(elapsed), + "maximum_elapsed_ns": max(elapsed), + "sat": sat_count, + "unsat": unsat_count, + "proof_kinds": dict(kinds), + "selected_mode": selected, + "bank_query_count": item["bank_query_count"], + "bank_pin_count": item["bank_pin_count"], + "bank_kernel": item["bank_kernel"], + "bank_tiles": item["bank_tiles"], + "auto_policy": policy, + "auto_policy_reason": reason, + "output_sha256": item["output_sha256"], + } + result = { + "schema": SCHEMA + ".audit", + "functional_complete": decisions is not None and len(decisions) == manifest["query_count"], + "source_commit": manifest["source_commit"], + "dataset_commit": manifest["dataset_commit"], + "dataset_instance_path": manifest["dataset_instance_path"], + "dataset_solution_path": manifest["dataset_solution_path"], + "evaluation_role": manifest["evaluation_role"], + "instance_sha256": manifest["instance_sha256"], + "solution_sha256": manifest["solution_sha256"], + "model": core.info, + "baseline_valid_full_itc_hard_constraints": True, + "flexible_exams": len(state["flexible_exams"]), + "protected_exams": len(state["protected_exams"]), + "queries": len(queries), + "warmups": manifest["warmups"], + "repeats": manifest["repeats"], + "timing_schedule": manifest["timing_schedule"], + "modes": summaries, + "auto_vs_raw_median_ratio": ( + summaries["auto"]["median_elapsed_ns"] / summaries["raw"]["median_elapsed_ns"] + ), + "sample_unsat": sample, + "limitations": [ + "conflict-core bounded repair, not complete ITC-2007 timetabling", + "room reassignment and soft objective are not solved", + "project-owned execution, not outside-team replication", + "ready-packet timing excludes candidate generation, packing and compilation", + ], + } + audit_bytes = canonical_json(result) + audit_path = root / "audit.json" + if audit_path.exists(): + expected = (root / "audit.sha256").read_text().strip() + existing = read_bound(audit_path, expected) + if existing != audit_bytes: + raise ValueError("existing audit differs from independent replay") + else: + write_once(audit_path, audit_bytes) + write_once(root / "audit.sha256", (digest(audit_bytes) + "\n").encode()) + return result + + +def main(argv: Iterable[str] | None = None) -> int: + parser = argparse.ArgumentParser() + parser.add_argument("command", choices=("freeze", "run", "audit")) + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--instance", type=Path) + parser.add_argument("--solution", type=Path) + parser.add_argument("--queries", type=int, default=1024) + parser.add_argument("--license", type=Path) + parser.add_argument("--dataset-instance-path", default=DEFAULT_INSTANCE) + parser.add_argument("--dataset-solution-path", default=DEFAULT_SOLUTION) + parser.add_argument( + "--evaluation-role", choices=("development", "holdout", "replication"), + default="development", + ) + args = parser.parse_args(argv) + if args.command == "freeze": + if args.instance is None or args.solution is None: + parser.error("freeze requires --instance and --solution") + value = freeze( + args.root, args.instance, args.solution, query_count=args.queries, + license_path=args.license, + dataset_instance_path=args.dataset_instance_path, + dataset_solution_path=args.dataset_solution_path, + evaluation_role=args.evaluation_role, + ) + elif args.command == "run": + value = run(args.root) + else: + value = audit(args.root) + print(json.dumps(value, indent=2, sort_keys=True, allow_nan=False)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/experiments/compiled_query/test_exact.py b/experiments/compiled_query/test_exact.py index c7042e0..4cc8d54 100644 --- a/experiments/compiled_query/test_exact.py +++ b/experiments/compiled_query/test_exact.py @@ -450,7 +450,10 @@ def test_tiled_witnesses_keep_high_colours_and_last_lane(exact_runtime, mode): if index % 3 and rng.random() < .3)) rows = tuple(rows) expected = [] - with exact_runtime.prepare(p, mode='scc' if mode == 'auto' else mode) as session: + # The bank averages well above eight pins per request, so structural-v2 + # intentionally selects raw rather than paying for tile64 propagation. + expected_mode = 'raw' if mode == 'auto' else mode + with exact_runtime.prepare(p, mode=expected_mode) as session: for query in rows: answer = session.solve(query) assert answer.status == 'SAT_VERIFIED' @@ -459,9 +462,29 @@ def test_tiled_witnesses_keep_high_colours_and_last_lane(exact_runtime, mode): for count in (64, 65, 129): answer = exact_runtime.solve_session_packet(pack_session_packet(p, rows[:count]), mode=mode) assert answer.sat_count == count and answer.proof_bytes == 0 + if mode == 'auto': + assert answer.info['selected_mode'] == 'raw' + assert answer.info['auto_policy'] == 'structural-v2' + assert answer.info['auto_policy_reason'] == 'high_pin_density' assert decode_certified_output(answer.output, n, count) == tuple(expected[:count]) +def test_auto_structural_v2_freezes_the_eight_pin_density_boundary(exact_runtime): + p = BinaryListProblem.create(16, 2, (), (3,) * 16) + low_rows = tuple(tuple((v, 3) for v in range(7)) for _ in range(64)) + high_rows = tuple(tuple((v, 3) for v in range(8)) for _ in range(64)) + low = exact_runtime.solve_session_packet(pack_session_packet(p, low_rows), mode='auto') + high = exact_runtime.solve_session_packet(pack_session_packet(p, high_rows), mode='auto') + assert low.info['selected_mode'] == 'scc' + assert low.info['auto_policy_reason'] == 'scc_tile64' + assert low.info['bank_query_count'] == 64 and low.info['bank_pin_count'] == 448 + assert high.info['selected_mode'] == 'raw' + assert high.info['auto_policy_reason'] == 'high_pin_density' + assert high.info['bank_query_count'] == 64 and high.info['bank_pin_count'] == 512 + assert high.output == exact_runtime.solve_session_packet( + pack_session_packet(p, high_rows), mode='raw').output + + @pytest.mark.parametrize('count', (0, 1, 63, 64, 65, 129)) @pytest.mark.parametrize('structure', ('empty', 'independent', 'chain', 'odd_cycle')) def test_auto_packet_exposes_and_matches_its_selected_engine(exact_runtime, count, structure): @@ -477,6 +500,8 @@ def test_auto_packet_exposes_and_matches_its_selected_engine(exact_runtime, coun selected = ('raw' if count < 64 or structure == 'odd_cycle' else 'route' if structure in ('chain', 'empty') else 'scc') assert answer.info['mode'] == 'auto' + assert answer.info['auto_policy'] == 'structural-v2' + assert answer.info['bank_query_count'] == count assert answer.info['selected_mode'] == selected assert answer.output == exact_runtime.solve_session_packet(packet, mode=selected).output for query, (decision, labels, certificate) in zip(rows, decode_certified_output(answer.output, n, count)): diff --git a/experiments/compiled_query/test_itc_exam_suite.py b/experiments/compiled_query/test_itc_exam_suite.py new file mode 100644 index 0000000..79b00aa --- /dev/null +++ b/experiments/compiled_query/test_itc_exam_suite.py @@ -0,0 +1,76 @@ +"""Contracts for the prospective multi-instance ITC transfer suite.""" +from __future__ import annotations + +import json +from pathlib import Path + +import pytest + +from experiments.compiled_query import itc_exam_suite as suite +from experiments.compiled_query import itc_exam_transfer as transfer +from experiments.compiled_query.test_itc_exam_transfer import INSTANCE, SOLUTION + + +def fixture_cases(upstream: Path) -> tuple[suite.SuiteCase, ...]: + records = ( + suite.SuiteCase("fixture-development", "development", "instances/dev.exam", "solutions/dev.sol"), + suite.SuiteCase("fixture-holdout-a", "holdout", "instances/a.exam", "solutions/a.sol"), + suite.SuiteCase("fixture-holdout-b", "holdout", "instances/b.exam", "solutions/b.sol"), + ) + (upstream / "instances").mkdir(parents=True) + (upstream / "solutions").mkdir(parents=True) + for case in records: + (upstream / case.instance_path).write_text(INSTANCE) + (upstream / case.solution_path).write_text(SOLUTION) + (upstream / "LICENSE").write_text("fixture redistribution license\n") + return records + + +def test_case_validation_rejects_escape_and_missing_holdout(): + with pytest.raises(ValueError, match="under upstream root"): + suite.SuiteCase("bad", "holdout", "../bad.exam", "x.sol").validate() + with pytest.raises(ValueError, match="holdout"): + suite._cases((suite.SuiteCase("dev", "development", "x.exam", "x.sol"),)) + + +def test_suite_freeze_run_and_audit(tmp_path, monkeypatch): + upstream = tmp_path / "upstream" + cases = fixture_cases(upstream) + root = tmp_path / "suite" + monkeypatch.setattr(transfer, "WARMUPS", 0) + monkeypatch.setattr(transfer, "REPEATS", 1) + manifest = suite.freeze( + root, upstream, cases=cases, query_count=8, + holdout_max_auto_raw_ratio=None, + source_commit="fixture-source", + ) + assert [item["role"] for item in manifest["cases"]] == [ + "development", "holdout", "holdout" + ] + assert manifest["holdout_max_auto_raw_ratio"] is None + run = suite.run(root) + assert len(run["cases"]) == 3 + result = suite.audit(root) + assert result["functional_complete"] + assert result["holdout_gate_pass"] + assert result["holdout_failures"] == [] + assert all(case["queries"] == 8 for case in result["cases"]) + assert all(case["auto_vs_raw_paired_median_ratio"] > 0 for case in result["cases"]) + assert json.loads((root / "suite-audit.json").read_text()) == result + + +def test_suite_detects_child_manifest_substitution(tmp_path, monkeypatch): + upstream = tmp_path / "upstream" + cases = fixture_cases(upstream) + root = tmp_path / "suite" + monkeypatch.setattr(transfer, "WARMUPS", 0) + monkeypatch.setattr(transfer, "REPEATS", 1) + suite.freeze( + root, upstream, cases=cases, query_count=8, + holdout_max_auto_raw_ratio=None, + source_commit="fixture-source", + ) + child = root / "cases" / cases[1].name / "manifest.sha256" + child.write_text("0" * 64 + "\n") + with pytest.raises(ValueError, match="manifest hash differs"): + suite.load_manifest(root) diff --git a/experiments/compiled_query/test_itc_exam_transfer.py b/experiments/compiled_query/test_itc_exam_transfer.py new file mode 100644 index 0000000..8795156 --- /dev/null +++ b/experiments/compiled_query/test_itc_exam_transfer.py @@ -0,0 +1,179 @@ +"""End-to-end contracts for the ITC exam bounded-repair transfer harness.""" +from __future__ import annotations + +import json +from pathlib import Path + +import pytest + +from experiments.compiled_query import itc_exam_transfer as transfer +from spectra.itc2007_exam import parse_itc2007_exam + + +INSTANCE = """[Exams:5] +120, 1, 2 +180, 2, 3 +120, 9 +120, 4 +120, 4 +[Periods:4] +01:01:2030, 09:00:00, 180, 0 +01:01:2030, 14:00:00, 120, 5 +02:01:2030, 09:00:00, 240, 0 +02:01:2030, 14:00:00, 180, 0 +[Rooms:1] +100, 0 +[PeriodHardConstraints] +0, EXCLUSION, 2 +1, AFTER, 0 +[RoomHardConstraints] +2, ROOM_EXCLUSIVE +[InstitutionalWeightings] +TWOINAROW, 30 +""" +SOLUTION = """0, 0 +2, 0 +1, 0 +0, 0 +3, 0 +""" + + +def write_inputs(tmp_path: Path) -> tuple[Path, Path, Path]: + instance = tmp_path / "fixture.exam" + solution = tmp_path / "fixture.sol" + instance.write_text(INSTANCE) + solution.write_text(SOLUTION) + license_path = tmp_path / "LICENSE" + license_path.write_text("fixture redistribution license\n") + return instance, solution, license_path + + +def test_solution_parser_and_full_hard_constraint_check(): + instance = parse_itc2007_exam(INSTANCE) + periods, rooms = transfer.parse_solution(SOLUTION, instance) + transfer.check_full_baseline(instance, periods, rooms) + assert periods == (0, 2, 1, 0, 3) + assert transfer.protected_exams(instance) == {0, 1, 2} + with pytest.raises(ValueError, match="AFTER"): + transfer.check_full_baseline(instance, (2, 0, 1, 0, 3), rooms) + + +def test_candidate_policy_retains_baseline_and_flexes_unprotected_exams(): + instance = parse_itc2007_exam(INSTANCE) + periods, _ = transfer.parse_solution(SOLUTION, instance) + candidates, flexible = transfer.derive_candidates(instance, periods, seed=7) + assert candidates[0] == (0,) and candidates[1] == (2,) and candidates[2] == (1,) + assert flexible == (3, 4) + assert all(periods[index] in candidates[index] for index in range(5)) + + +def test_freeze_run_and_independent_audit(tmp_path, monkeypatch): + instance, solution, license_path = write_inputs(tmp_path) + root = tmp_path / "evidence" + monkeypatch.setattr(transfer, "WARMUPS", 0) + monkeypatch.setattr(transfer, "REPEATS", 2) + manifest = transfer.freeze( + root, instance, solution, query_count=8, license_path=license_path, + source_commit="fixture-source", + dataset_instance_path="fixtures/custom.exam", + dataset_solution_path="fixtures/custom.sol", + evaluation_role="holdout", + ) + assert manifest["query_count"] == 8 + assert manifest["model"]["exams"] == 5 + assert manifest["dataset_instance_path"] == "fixtures/custom.exam" + assert manifest["dataset_solution_path"] == "fixtures/custom.sol" + assert manifest["evaluation_role"] == "holdout" + assert manifest["warmups"] == 0 and manifest["repeats"] == 2 + record = transfer.run(root) + assert tuple(record["modes"]) == transfer.MODES + assert record["warmups"] == 0 + assert record["execution_order"] == [ + ["auto", "raw", "scc", "route"], + ["raw", "scc", "route", "auto"], + ] + result = transfer.audit(root) + assert result["functional_complete"] + assert result["baseline_valid_full_itc_hard_constraints"] + assert result["flexible_exams"] == 2 + assert result["protected_exams"] == 3 + assert result["queries"] == 8 + assert result["evaluation_role"] == "holdout" + assert result["dataset_instance_path"] == "fixtures/custom.exam" + assert result["warmups"] == 0 and result["repeats"] == 2 + assert result["auto_vs_raw_median_ratio"] > 0 + assert all(item["sat"] + item["unsat"] == 8 for item in result["modes"].values()) + assert result["modes"]["auto"]["auto_policy"] == "structural-v2" + assert result["modes"]["auto"]["bank_query_count"] == 8 + assert result["modes"]["raw"]["auto_policy"] == "explicit" + assert json.loads((root / "audit.json").read_text()) == result + + + +def test_manifest_rejects_invalid_role_and_schedule(tmp_path): + instance, solution, license_path = write_inputs(tmp_path) + with pytest.raises(ValueError, match="evaluation_role"): + transfer.freeze( + tmp_path / "bad-role", instance, solution, query_count=8, + license_path=license_path, source_commit="fixture-source", + evaluation_role="training", + ) + + root = tmp_path / "evidence" + transfer.freeze( + root, instance, solution, query_count=8, license_path=license_path, + source_commit="fixture-source", + ) + manifest_path = root / "manifest.json" + manifest = json.loads(manifest_path.read_text()) + manifest["timing_schedule"] = "mode-by-mode" + payload = transfer.canonical_json(manifest) + manifest_path.write_bytes(payload) + (root / "manifest.sha256").write_text(transfer.digest(payload) + "\n") + with pytest.raises(ValueError, match="schedule differ"): + transfer.load_manifest(root) + +def test_frozen_artifact_hash_mutation_is_rejected(tmp_path): + instance, solution, license_path = write_inputs(tmp_path) + root = tmp_path / "evidence" + transfer.freeze(root, instance, solution, query_count=8, license_path=license_path, + source_commit="fixture-source") + packet = root / "session.sbc1" + packet.write_bytes(packet.read_bytes() + b"x") + with pytest.raises(ValueError, match="hash differs"): + transfer.load_manifest(root) + + +def test_audit_rederives_candidates_and_queries_instead_of_trusting_json(tmp_path): + instance, solution, license_path = write_inputs(tmp_path) + root = tmp_path / "evidence" + transfer.freeze(root, instance, solution, query_count=8, license_path=license_path, + source_commit="fixture-source") + + state_path = root / "candidates.json" + state = json.loads(state_path.read_text()) + state["seed"] += 1 + state_bytes = transfer.canonical_json(state) + state_path.write_bytes(state_bytes) + manifest_path = root / "manifest.json" + manifest = json.loads(manifest_path.read_text()) + manifest["artifacts"]["candidates.json"] = transfer.digest(state_bytes) + manifest_bytes = transfer.canonical_json(manifest) + manifest_path.write_bytes(manifest_bytes) + (root / "manifest.sha256").write_text(transfer.digest(manifest_bytes) + "\n") + + loaded = transfer.load_manifest(root) + with pytest.raises(ValueError, match="candidate policy differs"): + transfer.reconstruct(root, loaded) + + +def test_failed_freeze_does_not_publish_a_partial_evidence_root(tmp_path): + instance, solution, license_path = write_inputs(tmp_path) + root = tmp_path / "evidence" + solution.write_text("0, 0\n") + with pytest.raises(ValueError, match="row count differs"): + transfer.freeze(root, instance, solution, query_count=8, license_path=license_path, + source_commit="fixture-source") + assert not root.exists() + assert not root.with_name(root.name + ".partial").exists() diff --git a/scripts/check_exam_repair_installation.py b/scripts/check_exam_repair_installation.py new file mode 100644 index 0000000..f523897 --- /dev/null +++ b/scripts/check_exam_repair_installation.py @@ -0,0 +1,89 @@ +"""Exercise the installed exam-repair API without repository imports.""" +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import tempfile + +from spectra.exam_repair import ExamRepairModel +from spectra.exact import ExactRuntime, build_exact_runtime, verify_answer +from spectra.itc2007_exam import parse_itc2007_exam + +FIXTURE = """[Exams:3] +120, 7 +120, 7, 9 +120, 9 +[Periods:2] +01:01:2030, 09:00:00, 180, 0 +01:01:2030, 14:00:00, 180, 0 +[Rooms:1] +100, 0 +[PeriodHardConstraints] +[RoomHardConstraints] +[InstitutionalWeightings] +TWOINAROW, 1 +""" + + +def check() -> dict: + instance = parse_itc2007_exam(FIXTURE) + core = instance.conflict_core(((0, 1), (0, 1), (0, 1)), + acknowledge_ignored_constraints=True) + model: ExamRepairModel = core.model + sat_query = model.freeze_event(fixed=(("exam-0000", model.period_ids[0]),)) + unsat_query = model.freeze_event(fixed=( + ("exam-0000", model.period_ids[0]), + ("exam-0001", model.period_ids[0]), + )) + with tempfile.TemporaryDirectory(prefix="spectra-exam-install-") as directory: + library = build_exact_runtime(Path(directory) / "native") + runtime = ExactRuntime(library) + with runtime.prepare(model.problem, mode="scc") as session: + sat = session.solve(sat_query) + unsat = session.solve(unsat_query) + if sat.status != "SAT_VERIFIED" or unsat.status != "UNSAT_VERIFIED": + raise AssertionError("installed exact runtime returned unexpected status") + if not verify_answer(model.problem, sat_query, sat.status, sat.labels, sat.proof): + raise AssertionError("installed SAT result failed replay") + if not verify_answer( + model.problem, unsat_query, unsat.status, unsat.labels, unsat.proof): + raise AssertionError("installed UNSAT result failed replay") + assignment = model.assignment_from_labels(sat.labels, sat_query) + explanation = model.explain_unsat(unsat_query, unsat.proof) + info = session.info + if tuple(exam for exam, _period in assignment) != model.exam_ids: + raise AssertionError("installed repair omitted or reordered exams") + if not model.check_assignment(assignment, sat_query): + raise AssertionError("installed named assignment failed adapter replay") + if explanation.get("kind") not in ("EMPTY_DOMAIN", "FORCED_OPPOSITES"): + raise AssertionError("installed repair returned an unknown proof explanation") + return { + "schema": "spectra.exam_repair.installation_check.v1", + "exams": model.problem.n, + "periods": model.problem.palette, + "conflicts": len(model.problem.edges), + "sat_status": sat.status, + "unsat_status": unsat.status, + "proof_kind": explanation["kind"], + "assignment_complete": True, + "native_abi": info["native_abi"], + "exact_api_version": info["exact_api_version"], + } + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--out", type=Path) + args = parser.parse_args() + result = check() + payload = json.dumps(result, indent=2, sort_keys=True) + "\n" + if args.out is not None: + args.out.parent.mkdir(parents=True, exist_ok=True) + args.out.write_text(payload, encoding="utf-8") + print(payload, end="") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/spectra/_native/exact_binary.cpp b/spectra/_native/exact_binary.cpp index 96182ee..96694d3 100644 --- a/spectra/_native/exact_binary.cpp +++ b/spectra/_native/exact_binary.cpp @@ -341,12 +341,13 @@ struct ObserverSessionPacket { struct Answer {bool sat=true;std::vector labels,certificate;W visits=0,arcs=0;}; struct Index { U n,k,nc=0,bad=NIL;bool condensed,sparse,routed;W cap;W full_arcs=0; + const char* auto_reason="explicit_mode"; std::vector masks; std::vector lo,hi,base; std::vector comp,opposite; std::vector inward,outward,representative,cross_from,cross_to; CSR original,dag; - Index(Input p,U mode,U bank_size=0):n(p.n),k(p.k),condensed(mode!=0),sparse(mode==2),routed(mode==3),cap(p.cap),masks(std::move(p.masks)),lo(n),hi(n,255),base(n) { + Index(Input p,U mode,U bank_size=0,U bank_pins=0):n(p.n),k(p.k),condensed(mode!=0),sparse(mode==2),routed(mode==3),cap(p.cap),masks(std::move(p.masks)),lo(n),hi(n,255),base(n) { std::vector> arcs;arcs.reserve(4*p.edges.size()+n); for(U v=0;v(comp[2*v]=64 && bad==NIL; - routed=condensed && nc<=n; + // Structural auto policy v2. set9 development evidence showed that + // large, heavily pinned repair banks can lose to raw traversal even + // when tile64 is available. The policy therefore uses only immutable + // request geometry (never answers or measured timings): raw below one + // tile, on a base contradiction, or at eight pins per row and above. + // The threshold is frozen before the set10/set12 holdout transfer. + if(bank_size<64) { + condensed=false;routed=false;auto_reason="bank_below_tile64"; + } else if(bad!=NIL) { + condensed=false;routed=false;auto_reason="base_infeasible"; + } else if(W(bank_pins)>=8*W(bank_size)) { + condensed=false;routed=false;auto_reason="high_pin_density"; + } else { + condensed=true;routed=nc<=n; + auto_reason=routed?"route_component_compression":"scc_tile64"; + } } if(condensed && bad==NIL) { opposite.assign(nc,NIL); @@ -990,6 +1002,7 @@ PyObject* solve_session_packet(PyObject*,PyObject* args) { const auto* data=reinterpret_cast(PyBytes_AS_STRING(packet)); size_t size=static_cast(PyBytes_GET_SIZE(packet)); std::unique_ptr solver;std::unique_ptr observer;BankResult result; + U bank_queries=0,bank_pins=0; { Released guard; ObserverSessionPacket observer_rows(data,size,limit); @@ -997,8 +1010,9 @@ PyObject* solve_session_packet(PyObject*,PyObject* args) { W observer_bytes=observer->bytes(); if(observer_bytes>=limit) throw Resource("SBC1 observer consumes session cap"); SolverSessionPacket solver_rows(data,size,limit-observer_bytes); + bank_queries=solver_rows.query_count();bank_pins=solver_rows.pin_count(); solver=std::make_unique(solver_rows.take_input(limit-observer_bytes), - static_cast(which),solver_rows.query_count()); + static_cast(which),bank_queries,bank_pins); result=run_rows(*solver,*observer,solver_rows,observer_rows,size,limit,"SBC1"); } PyObject* output=PyBytes_FromStringAndSize(reinterpret_cast(result.output.data()), @@ -1018,11 +1032,22 @@ PyObject* solve_session_packet(PyObject*,PyObject* args) { if(!metadata){Py_DECREF(output);return nullptr;} PyObject* kernel=PyUnicode_FromString(result.tiled?"tile64":"serial"); PyObject* tiles=PyLong_FromUnsignedLong(result.tiles); - if(!kernel || !tiles || PyDict_SetItemString(metadata,"bank_kernel",kernel)<0 - || PyDict_SetItemString(metadata,"bank_tiles",tiles)<0) { - Py_XDECREF(kernel);Py_XDECREF(tiles);Py_DECREF(output);Py_DECREF(metadata);return nullptr; + PyObject* queries=PyLong_FromUnsignedLong(bank_queries); + PyObject* pins=PyLong_FromUnsignedLong(bank_pins); + PyObject* policy=PyUnicode_FromString(which==4?"structural-v2":"explicit"); + PyObject* reason=PyUnicode_FromString(solver->auto_reason); + if(!kernel || !tiles || !queries || !pins || !policy || !reason + || PyDict_SetItemString(metadata,"bank_kernel",kernel)<0 + || PyDict_SetItemString(metadata,"bank_tiles",tiles)<0 + || PyDict_SetItemString(metadata,"bank_query_count",queries)<0 + || PyDict_SetItemString(metadata,"bank_pin_count",pins)<0 + || PyDict_SetItemString(metadata,"auto_policy",policy)<0 + || PyDict_SetItemString(metadata,"auto_policy_reason",reason)<0) { + Py_XDECREF(kernel);Py_XDECREF(tiles);Py_XDECREF(queries);Py_XDECREF(pins); + Py_XDECREF(policy);Py_XDECREF(reason);Py_DECREF(output);Py_DECREF(metadata);return nullptr; } - Py_DECREF(kernel);Py_DECREF(tiles); + Py_DECREF(kernel);Py_DECREF(tiles);Py_DECREF(queries);Py_DECREF(pins); + Py_DECREF(policy);Py_DECREF(reason); return Py_BuildValue("NKKKKKKN",output, static_cast(result.sat+result.unsat), static_cast(result.sat), diff --git a/spectra/exact.py b/spectra/exact.py index 983d335..60ed10e 100644 --- a/spectra/exact.py +++ b/spectra/exact.py @@ -402,11 +402,13 @@ def solve_session_packet(self, packet: bytes, *, mode: str = 'route', max_bytes: int = DEFAULT_BYTES) -> ExactSessionPacketAnswer: """Construct, solve, verify, serialize and dispose one prepared SBC1 session. - Opt-in ``auto`` uses raw traversal below 64 requests or on a base-infeasible - graph. Otherwise it uses routed proofs when SCCs at least halve the - literal graph, and plain SCC batching when they do not. Selection uses - fixed-graph structure and bank length, never measured answers or timings. - ``info['selected_mode']`` names the actual engine. Explicit modes and the + Opt-in ``auto`` uses structural policy v2. It selects raw traversal below + 64 requests, on a base-infeasible graph, or when the immutable bank carries + at least eight pins per request on average. Otherwise it uses routed proofs + when SCCs at least halve the literal graph, and plain SCC batching when they + do not. Selection uses only admitted graph/request geometry, never answers + or measured timings. ``info['selected_mode']`` and + ``info['auto_policy_reason']`` expose the decision. Explicit modes and the default retain their meaning; this policy is an experimental heuristic. """ if type(packet) is not bytes: diff --git a/spectra/exam_repair.py b/spectra/exam_repair.py new file mode 100644 index 0000000..121ae9e --- /dev/null +++ b/spectra/exam_repair.py @@ -0,0 +1,418 @@ +"""Proof-carrying bounded exam rescheduling on top of :mod:`spectra.exact`. + +This adapter models a deliberately narrow operational problem: every exam has one +or two pre-approved candidate periods, and exams that share a student (or an +explicit exclusion) may not use the same period. Period closures and exam pins +become current-query restrictions. The exact backend can then return either a +complete checked repair or a portable contradiction proof. + +Room capacities, room exclusivity, precedence, coincidence, soft penalties and +candidate generation are outside this model. They must be handled before +admission or checked separately; the module never silently drops them. +""" +from __future__ import annotations + +from dataclasses import dataclass +from typing import Iterable, Mapping, Sequence + +from .exact_problem import ExactProblem +from .exact_proof import decode_proof, verify_answer + +MAX_IDENTIFIER_CHARS = 512 +MAX_ENROLLMENTS = 4_000_000 +MAX_CONFLICTS = 2_000_000 +MAX_PAIR_EMISSIONS = 8_000_000 + + +@dataclass(frozen=True, slots=True) +class ExamConflictReason: + """One deterministic witness for an admitted conflict edge.""" + + kind: str + student: int | None = None + + def __post_init__(self) -> None: + if self.kind not in ("shared_student", "explicit_exclusion"): + raise ValueError("unsupported exam conflict reason") + if self.kind == "shared_student": + if type(self.student) is not int or not 0 <= self.student < 1 << 63: + raise ValueError("shared-student reason needs a bounded student id") + elif self.student is not None: + raise ValueError("explicit exclusion must not carry a student id") + + +@dataclass(frozen=True, slots=True) +class ExamRepairModel: + """A checked reduction from bounded exam repair to exact list colouring. + + ``problem`` uses exam positions as vertices and period positions as colours. + ``conflict_reasons`` is aligned with ``problem.edges`` and is explanatory + metadata only; the exact solver and verifier receive the original graph. + """ + + problem: ExactProblem + exam_ids: tuple[str, ...] + period_ids: tuple[str, ...] + students: tuple[tuple[int, ...], ...] + conflict_reasons: tuple[ExamConflictReason, ...] + + @classmethod + def create( + cls, + period_ids: Iterable[str], + exam_ids: Iterable[str], + students: Iterable[Iterable[int]], + candidate_periods: Iterable[Iterable[str]], + *, + exclusions: Iterable[Sequence[str]] = (), + ) -> "ExamRepairModel": + periods = cls._bounded_snapshot(period_ids, 64, "period identifier") + exams = cls._bounded_snapshot(exam_ids, 100_000, "exam identifier") + student_rows: list[tuple[int, ...]] = [] + try: + for row in students: + if len(student_rows) >= len(exams): + raise ValueError("exam, student and candidate row counts differ") + if isinstance(row, (str, bytes)): + raise ValueError("student rows must be integer iterables, not strings") + student_rows.append(tuple(row)) + except TypeError as error: + raise ValueError("student rows must be iterable") from error + candidate_rows: list[tuple[str, ...]] = [] + try: + iterator = iter(candidate_periods) + except TypeError as error: + raise ValueError("candidate rows must be iterable") from error + for row in iterator: + if len(candidate_rows) >= len(exams): + raise ValueError("exam, student and candidate row counts differ") + if isinstance(row, (str, bytes)): + raise ValueError("candidate rows must be period iterables, not strings") + try: + candidate_rows.append(tuple(row)) + except TypeError as error: + raise ValueError("candidate rows must be iterable") from error + if not 1 <= len(periods) <= 64: + raise ValueError("period count must be in [1, 64]") + if len(student_rows) != len(exams) or len(candidate_rows) != len(exams): + raise ValueError("exam, student and candidate row counts differ") + candidates = tuple(candidate_rows) + cls._check_identifiers(periods, "period") + cls._check_identifiers(exams, "exam") + if len(set(periods)) != len(periods): + raise ValueError("period identifiers must be unique") + if len(set(exams)) != len(exams): + raise ValueError("exam identifiers must be unique") + + period_at = {name: index for index, name in enumerate(periods)} + masks: list[int] = [] + for row in candidates: + if not 1 <= len(row) <= 2 or any(type(value) is not str for value in row): + raise ValueError("each exam needs one or two exact period identifiers") + if len(set(row)) != len(row): + raise ValueError("candidate periods must be unique within an exam") + try: + indices = tuple(period_at[value] for value in row) + except KeyError as error: + raise ValueError("candidate names an unknown period") from error + masks.append(sum(1 << index for index in indices)) + + normalized_students: list[tuple[int, ...]] = [] + enrollment_total = 0 + roster: dict[int, list[int]] = {} + for vertex, row in enumerate(student_rows): + if any(type(value) is not int or not 0 <= value < 1 << 63 for value in row): + raise ValueError("student identifiers must be bounded nonnegative integers") + normalized = tuple(sorted(set(row))) + enrollment_total += len(normalized) + if enrollment_total > MAX_ENROLLMENTS: + raise ValueError("enrollment count exceeds adapter bound") + normalized_students.append(normalized) + for student in normalized: + roster.setdefault(student, []).append(vertex) + + reasons: dict[tuple[int, int], ExamConflictReason] = {} + pair_emissions = 0 + for student in sorted(roster): + vertices = roster[student] + # A single very large cohort can otherwise spend unbounded time before + # the native edge limit is reached. This is a conservative admission + # check; duplicate pairs from other students do not earn extra budget. + pairs = len(vertices) * (len(vertices) - 1) // 2 + pair_emissions += pairs + if pairs > MAX_CONFLICTS or pair_emissions > MAX_PAIR_EMISSIONS: + raise ValueError("student cohorts exceed bounded conflict-generation work") + for offset, left in enumerate(vertices): + for right in vertices[offset + 1:]: + edge = (left, right) + reasons.setdefault(edge, ExamConflictReason("shared_student", student)) + if len(reasons) > MAX_CONFLICTS: + raise ValueError("conflict edge count exceeds native bound") + + exam_at = {name: index for index, name in enumerate(exams)} + for item in exclusions: + if isinstance(item, (str, bytes)): + raise ValueError("exclusion must contain two exact exam identifiers") + row = tuple(item) + if len(row) != 2 or any(type(value) is not str for value in row): + raise ValueError("exclusion must contain two exact exam identifiers") + try: + left, right = exam_at[row[0]], exam_at[row[1]] + except KeyError as error: + raise ValueError("exclusion names an unknown exam") from error + if left == right: + raise ValueError("an exam cannot exclude itself") + edge = (min(left, right), max(left, right)) + reasons.setdefault(edge, ExamConflictReason("explicit_exclusion")) + if len(reasons) > MAX_CONFLICTS: + raise ValueError("conflict edge count exceeds native bound") + + edges = tuple(sorted(reasons)) + problem = ExactProblem.create(len(exams), len(periods), edges, masks) + return cls( + problem=problem, + exam_ids=exams, + period_ids=periods, + students=tuple(normalized_students), + conflict_reasons=tuple(reasons[edge] for edge in problem.edges), + ) + + @staticmethod + def _bounded_snapshot(values: Iterable[str], limit: int, name: str) -> tuple[str, ...]: + try: + iterator = iter(values) + except TypeError as error: + raise ValueError(f"{name}s must be iterable") from error + copied: list[str] = [] + for value in iterator: + if len(copied) >= limit: + raise ValueError(f"{name} count exceeds adapter bound") + copied.append(value) + return tuple(copied) + + @staticmethod + def _check_identifiers(values: tuple[str, ...], kind: str) -> None: + for value in values: + if type(value) is not str or not value or len(value) > MAX_IDENTIFIER_CHARS: + raise ValueError( + f"{kind} identifiers must be nonempty exact strings of at most " + f"{MAX_IDENTIFIER_CHARS} characters" + ) + + @property + def candidates(self) -> tuple[tuple[str, ...], ...]: + """Return each exam's admitted candidate periods in colour order.""" + return tuple( + tuple(self.period_ids[index] for index in range(len(self.period_ids)) if mask >> index & 1) + for mask in self.problem.masks + ) + + @property + def info(self) -> dict: + shared = sum(reason.kind == "shared_student" for reason in self.conflict_reasons) + return { + "schema": "spectra.exam_repair.model.v1", + "exams": self.problem.n, + "periods": self.problem.palette, + "conflicts": len(self.problem.edges), + "shared_student_conflicts": shared, + "explicit_exclusions": len(self.problem.edges) - shared, + "enrollments": sum(map(len, self.students)), + "scope": "one/two pre-approved periods; shared students and explicit exclusions only", + } + + def freeze_event( + self, + *, + closed_periods: Iterable[str] = (), + fixed: Iterable[Sequence[str]] = (), + allowed: Iterable[Sequence[object]] = (), + ) -> tuple[tuple[int, int], ...]: + """Translate current closures/pins into one canonical exact query. + + ``fixed`` rows are ``(exam_id, period_id)``. ``allowed`` rows are + ``(exam_id, iterable_of_period_ids)``. Multiple sources of restriction + are intersected. A zero resulting mask is retained so the exact service + can return a portable empty-domain proof instead of raising. + """ + period_at = {name: index for index, name in enumerate(self.period_ids)} + exam_at = {name: index for index, name in enumerate(self.exam_ids)} + closed = tuple(closed_periods) + if any(type(name) is not str for name in closed) or len(set(closed)) != len(closed): + raise ValueError("closed periods must be unique exact strings") + try: + closed_mask = sum(1 << period_at[name] for name in closed) + except KeyError as error: + raise ValueError("closed-period event names an unknown period") from error + + effective = [mask & ~closed_mask for mask in self.problem.masks] + fixed_seen: set[int] = set() + for item in fixed: + if isinstance(item, (str, bytes)): + raise ValueError("fixed rows must be exact exam/period string pairs") + row = tuple(item) + if len(row) != 2 or any(type(value) is not str for value in row): + raise ValueError("fixed rows must be exact exam/period string pairs") + try: + vertex, period = exam_at[row[0]], period_at[row[1]] + except KeyError as error: + raise ValueError("fixed row names an unknown exam or period") from error + if vertex in fixed_seen: + raise ValueError("an exam appears more than once in fixed rows") + fixed_seen.add(vertex) + effective[vertex] &= 1 << period + + allowed_seen: set[int] = set() + for item in allowed: + if isinstance(item, (str, bytes)): + raise ValueError("allowed rows must contain an exam and a period iterable") + row = tuple(item) + if len(row) != 2 or type(row[0]) is not str or isinstance(row[1], (str, bytes)): + raise ValueError("allowed rows must contain an exam and a period iterable") + try: + vertex = exam_at[row[0]] + except KeyError as error: + raise ValueError("allowed row names an unknown exam") from error + if vertex in allowed_seen: + raise ValueError("an exam appears more than once in allowed rows") + allowed_seen.add(vertex) + try: + choices = tuple(row[1]) + except TypeError as error: + raise ValueError( + "allowed rows must contain an exam and a period iterable" + ) from error + if any(type(name) is not str for name in choices) or len(set(choices)) != len(choices): + raise ValueError("allowed period identifiers must be unique exact strings") + try: + mask = sum(1 << period_at[name] for name in choices) + except KeyError as error: + raise ValueError("allowed row names an unknown period") from error + effective[vertex] &= mask + + return tuple( + (vertex, mask) + for vertex, (original, mask) in enumerate(zip(self.problem.masks, effective)) + if mask != original + ) + + def assignment_from_labels( + self, + labels: bytes, + query: Iterable[Sequence[int]] = (), + ) -> tuple[tuple[str, str], ...]: + """Decode a complete checked label vector into stable exam/period rows.""" + frozen = self.problem.freeze_query(query) + if not self.problem.check(labels, frozen): + raise ValueError("labels do not satisfy the admitted repair problem and event") + return tuple((exam, self.period_ids[labels[index]]) + for index, exam in enumerate(self.exam_ids)) + + def check_assignment( + self, + assignment: Mapping[str, str] | Iterable[Sequence[str]], + query: Iterable[Sequence[int]] = (), + ) -> bool: + """Check a named assignment against original adapter inputs only.""" + try: + rows = tuple(assignment.items()) if isinstance(assignment, Mapping) else tuple( + tuple(row) for row in assignment + ) + exam_at = {name: index for index, name in enumerate(self.exam_ids)} + period_at = {name: index for index, name in enumerate(self.period_ids)} + labels = [-1] * self.problem.n + for row in rows: + if len(row) != 2 or any(type(value) is not str for value in row): + return False + vertex = exam_at[row[0]] + if labels[vertex] != -1: + return False + labels[vertex] = period_at[row[1]] + if any(value < 0 for value in labels): + return False + return self.problem.check(bytes(labels), query) + except (KeyError, TypeError, ValueError, OverflowError): + return False + + def explain_unsat( + self, + query: Iterable[Sequence[int]], + certificate: bytes, + ) -> dict: + """Verify and translate an SBP1 contradiction into named repair steps.""" + frozen = self.problem.freeze_query(query) + if not verify_answer(self.problem, frozen, "UNSAT_VERIFIED", b"", certificate): + raise ValueError("certificate does not prove this repair event infeasible") + kind, left, right = decode_proof(certificate, self.problem.n) + effective = list(self.problem.masks) + for vertex, mask in frozen: + effective[vertex] &= mask + if kind == 1: + vertex = left[0] + return { + "schema": "spectra.exam_repair.proof.v1", + "kind": "EMPTY_DOMAIN", + "exam": self.exam_ids[vertex], + "original_candidates": list(self.candidates[vertex]), + "remaining_candidates": [], + } + return { + "schema": "spectra.exam_repair.proof.v1", + "kind": "FORCED_OPPOSITES" if kind == 2 else "BASE_CONTRADICTION", + "left": self._explain_path(left, effective), + "right": self._explain_path(right, effective), + } + + def _literal(self, literal: int) -> dict: + vertex, side = divmod(literal, 2) + colors = tuple(index for index in range(self.problem.palette) + if self.problem.masks[vertex] >> index & 1) + period = self.period_ids[colors[side]] if side < len(colors) else None + return { + "literal": literal, + "exam": self.exam_ids[vertex], + "exam_index": vertex, + "period": period, + "side": side, + } + + def _explain_path(self, path: tuple[int, ...], effective: list[int]) -> dict: + reasons = {edge: reason for edge, reason in zip( + self.problem.edges, self.conflict_reasons + )} + nodes = [self._literal(literal) for literal in path] + steps = [] + for before, after in zip(path, path[1:]): + left, right = before // 2, after // 2 + if left == right: + steps.append({ + "kind": "SINGLETON_DOMAIN", + "exam": self.exam_ids[left], + "therefore_period": self._literal(after)["period"], + }) + continue + edge = (min(left, right), max(left, right)) + reason = reasons[edge] + before_node = self._literal(before) + after_node = self._literal(after) + step = { + "kind": "SHARED_STUDENT_CONFLICT" if reason.kind == "shared_student" + else "EXPLICIT_EXCLUSION", + "from_exam": before_node["exam"], + "from_period": before_node["period"], + "to_exam": after_node["exam"], + "therefore_period": after_node["period"], + } + if reason.student is not None: + step["student"] = reason.student + steps.append(step) + root = nodes[0] + root_mask = effective[root["exam_index"]] + root["forced_by_event"] = ( + root["period"] is not None + and root_mask == 1 << self.period_ids.index(root["period"]) + ) + return {"nodes": nodes, "steps": steps} + + +__all__ = ["ExamConflictReason", "ExamRepairModel"] diff --git a/spectra/itc2007_exam.py b/spectra/itc2007_exam.py new file mode 100644 index 0000000..b0f9c52 --- /dev/null +++ b/spectra/itc2007_exam.py @@ -0,0 +1,316 @@ +"""Strict ITC 2007 examination-timetabling reader and bounded-repair adapter. + +The reader preserves the complete hard/soft section inventory. Conversion to +:class:`spectra.exam_repair.ExamRepairModel` is intentionally explicit about the +smaller problem being solved: shared-student and EXCLUSION conflicts over one or +two pre-approved periods per exam. AFTER, EXAM_COINCIDENCE, room assignment, +room exclusivity, and soft objectives are never silently interpreted as solved. +""" +from __future__ import annotations + +from dataclasses import dataclass +import hashlib +import re +from typing import Iterable + +from .exam_repair import ExamRepairModel + +_HEADER = re.compile(r"^\[([A-Za-z]+)(?::([0-9]+))?\]$") +_REQUIRED = ( + "Exams", "Periods", "Rooms", "PeriodHardConstraints", + "RoomHardConstraints", "InstitutionalWeightings", +) + + +@dataclass(frozen=True, slots=True) +class ITCExam: + duration: int + students: tuple[int, ...] + + +@dataclass(frozen=True, slots=True) +class ITCPeriod: + date: str + time: str + duration: int + penalty: int + + @property + def identifier(self) -> str: + return f"{self.date} {self.time}" + + +@dataclass(frozen=True, slots=True) +class ITCRoom: + capacity: int + penalty: int + + +@dataclass(frozen=True, slots=True) +class ITCConflictCore: + """One exact conflict-core model plus an immutable omitted-constraint receipt.""" + + model: ExamRepairModel + source_sha256: str + ignored_constraints: tuple[tuple[str, int], ...] + + @property + def info(self) -> dict: + return { + **self.model.info, + "schema": "spectra.itc2007.conflict_core.v1", + "source_sha256": self.source_sha256, + "ignored_constraints": dict(self.ignored_constraints), + "claim_boundary": ( + "bounded candidate-period repair only; not a complete ITC 2007 " + "timetable, room assignment, precedence solution or soft-score result" + ), + } + + +@dataclass(frozen=True, slots=True) +class ITCExamInstance: + exams: tuple[ITCExam, ...] + periods: tuple[ITCPeriod, ...] + rooms: tuple[ITCRoom, ...] + after: tuple[tuple[int, int], ...] + exclusions: tuple[tuple[int, int], ...] + coincidences: tuple[tuple[int, int], ...] + room_exclusive: tuple[int, ...] + institutional_weightings: tuple[tuple[str, ...], ...] + source_sha256: str + + @property + def ignored_constraint_counts(self) -> tuple[tuple[str, int], ...]: + values = ( + ("after", len(self.after)), + ("exam_coincidence", len(self.coincidences)), + ("room_exclusive", len(self.room_exclusive)), + ("room_assignment", len(self.rooms)), + ("period_penalties", sum(period.penalty != 0 for period in self.periods)), + ("room_penalties", sum(room.penalty != 0 for room in self.rooms)), + ("institutional_weightings", len(self.institutional_weightings)), + ) + return tuple((name, count) for name, count in values if count) + + def conflict_core( + self, + candidate_periods: Iterable[Iterable[int]], + *, + acknowledge_ignored_constraints: bool = False, + ) -> ITCConflictCore: + """Build the exact shared-student/EXCLUSION repair core. + + Candidate rows contain one or two period indices and are assumed to have + been pre-approved by the operator. Period duration is still checked here. + Any unmodelled ITC section requires an explicit acknowledgement flag and + remains listed in the returned receipt. + """ + ignored = self.ignored_constraint_counts + if ignored and not acknowledge_ignored_constraints: + names = ", ".join(name for name, _ in ignored) + raise ValueError("ITC conflict-core conversion would ignore: " + names) + copied_rows: list[tuple[int, ...]] = [] + try: + for row in candidate_periods: + if len(copied_rows) >= len(self.exams): + raise ValueError("candidate row count differs from exam count") + if isinstance(row, (str, bytes)): + raise ValueError("candidate rows must be period-index iterables") + copied_rows.append(tuple(row)) + except TypeError as error: + raise ValueError("candidate rows must be period-index iterables") from error + rows = tuple(copied_rows) + if len(rows) != len(self.exams): + raise ValueError("candidate row count differs from exam count") + names = tuple( + f"{index:02d}:{period.identifier}" + for index, period in enumerate(self.periods) + ) + candidates: list[tuple[str, ...]] = [] + for exam_index, (exam, row) in enumerate(zip(self.exams, rows)): + if not 1 <= len(row) <= 2 or any(type(value) is not int for value in row): + raise ValueError("every exam needs one or two exact period indices") + if len(set(row)) != len(row) or any(not 0 <= value < len(self.periods) for value in row): + raise ValueError("candidate period indices must be unique and in range") + if any(self.periods[value].duration < exam.duration for value in row): + raise ValueError(f"exam {exam_index} does not fit a candidate period") + candidates.append(tuple(names[value] for value in row)) + exam_ids = tuple(f"exam-{index:04d}" for index in range(len(self.exams))) + exclusions = tuple((exam_ids[left], exam_ids[right]) + for left, right in self.exclusions) + model = ExamRepairModel.create( + names, + exam_ids, + (exam.students for exam in self.exams), + candidates, + exclusions=exclusions, + ) + return ITCConflictCore(model, self.source_sha256, ignored) + + +def parse_itc2007_exam(text: str, *, max_chars: int = 64 << 20) -> ITCExamInstance: + """Parse one canonical ITC 2007 examination instance without dependencies.""" + if type(text) is not str: + raise ValueError("ITC input must be an exact string") + if type(max_chars) is not int or not 1 <= max_chars <= 1 << 30: + raise ValueError("max_chars must be a bounded positive exact integer") + if len(text) > max_chars: + raise ValueError("ITC input exceeds max_chars") + normalized = text.removeprefix("\ufeff").replace("\r\n", "\n").replace("\r", "\n") + sections: dict[str, tuple[int | None, list[tuple[int, str]]]] = {} + current: str | None = None + for line_number, raw in enumerate(normalized.split("\n"), 1): + line = raw.strip() + if not line: + continue + match = _HEADER.fullmatch(line) + if match: + name, count_text = match.groups() + if name not in _REQUIRED: + raise ValueError(f"line {line_number}: unknown ITC section {name}") + if name in sections: + raise ValueError(f"line {line_number}: duplicate ITC section {name}") + expected = int(count_text) if count_text is not None else None + if name in ("Exams", "Periods", "Rooms") and expected is None: + raise ValueError(f"line {line_number}: {name} requires a count") + if name not in ("Exams", "Periods", "Rooms") and expected is not None: + raise ValueError(f"line {line_number}: {name} must not carry a count") + sections[name] = (expected, []) + current = name + continue + if current is None: + raise ValueError(f"line {line_number}: data appears before a section header") + sections[current][1].append((line_number, line)) + if tuple(sections) != _REQUIRED: + raise ValueError("ITC sections are missing, duplicated or out of canonical order") + for name in ("Exams", "Periods", "Rooms"): + expected, rows = sections[name] + if expected != len(rows): + raise ValueError(f"{name} row count differs: expected {expected}, found {len(rows)}") + + exams: list[ITCExam] = [] + for line_number, line in sections["Exams"][1]: + fields = _fields(line, line_number) + if len(fields) < 1: + raise ValueError(f"line {line_number}: empty exam row") + duration = _nonnegative(fields[0], line_number, "exam duration", positive=True) + student_values = tuple(_nonnegative(value, line_number, "student id") + for value in fields[1:]) + if len(set(student_values)) != len(student_values): + raise ValueError(f"line {line_number}: duplicate student within an exam") + exams.append(ITCExam(duration, tuple(sorted(student_values)))) + + periods: list[ITCPeriod] = [] + for line_number, line in sections["Periods"][1]: + fields = _fields(line, line_number) + if len(fields) != 4 or not fields[0] or not fields[1]: + raise ValueError(f"line {line_number}: period row must have date,time,duration,penalty") + periods.append(ITCPeriod( + fields[0], fields[1], + _nonnegative(fields[2], line_number, "period duration", positive=True), + _nonnegative(fields[3], line_number, "period penalty"), + )) + identifiers = tuple(period.identifier for period in periods) + if len(set(identifiers)) != len(identifiers): + raise ValueError("period date/time identifiers must be unique") + + rooms: list[ITCRoom] = [] + for line_number, line in sections["Rooms"][1]: + fields = _fields(line, line_number) + if len(fields) != 2: + raise ValueError(f"line {line_number}: room row must have capacity,penalty") + rooms.append(ITCRoom( + _nonnegative(fields[0], line_number, "room capacity"), + _nonnegative(fields[1], line_number, "room penalty"), + )) + + after: list[tuple[int, int]] = [] + exclusions: list[tuple[int, int]] = [] + coincidences: list[tuple[int, int]] = [] + seen_hard: set[tuple[int, str, int]] = set() + for line_number, line in sections["PeriodHardConstraints"][1]: + fields = _fields(line, line_number) + if len(fields) != 3: + raise ValueError(f"line {line_number}: period hard constraint must have three fields") + left = _exam_index(fields[0], line_number, len(exams)) + right = _exam_index(fields[2], line_number, len(exams)) + relation = fields[1] + if relation not in ("AFTER", "EXCLUSION", "EXAM_COINCIDENCE"): + raise ValueError(f"line {line_number}: unsupported period relation {relation}") + if left == right: + raise ValueError(f"line {line_number}: hard constraint repeats one exam") + key = ((min(left, right), relation, max(left, right)) + if relation in ("EXCLUSION", "EXAM_COINCIDENCE") + else (left, relation, right)) + if key in seen_hard: + raise ValueError(f"line {line_number}: duplicate period hard constraint") + seen_hard.add(key) + if relation == "AFTER": + after.append((left, right)) + elif relation == "EXCLUSION": + exclusions.append((min(left, right), max(left, right))) + else: + coincidences.append((min(left, right), max(left, right))) + + room_exclusive: list[int] = [] + for line_number, line in sections["RoomHardConstraints"][1]: + fields = _fields(line, line_number) + if len(fields) != 2 or fields[1] != "ROOM_EXCLUSIVE": + raise ValueError(f"line {line_number}: invalid room hard constraint") + exam = _exam_index(fields[0], line_number, len(exams)) + if exam in room_exclusive: + raise ValueError(f"line {line_number}: duplicate room-exclusive exam") + room_exclusive.append(exam) + + weightings: list[tuple[str, ...]] = [] + for line_number, line in sections["InstitutionalWeightings"][1]: + fields = _fields(line, line_number) + if not fields[0] or any(not value for value in fields): + raise ValueError(f"line {line_number}: empty institutional weighting field") + # The exact weighting vocabulary is not interpreted by the conflict-core + # adapter, but every numeric payload must still be canonical and bounded. + for value in fields[1:]: + _nonnegative(value, line_number, "institutional weighting") + weightings.append(fields) + + return ITCExamInstance( + exams=tuple(exams), + periods=tuple(periods), + rooms=tuple(rooms), + after=tuple(after), + exclusions=tuple(sorted(set(exclusions))), + coincidences=tuple(sorted(set(coincidences))), + room_exclusive=tuple(room_exclusive), + institutional_weightings=tuple(weightings), + source_sha256=hashlib.sha256(normalized.encode("utf-8")).hexdigest(), + ) + + +def _fields(line: str, line_number: int) -> tuple[str, ...]: + values = tuple(value.strip() for value in line.split(",")) + if any(not value for value in values): + raise ValueError(f"line {line_number}: empty comma-separated field") + return values + + +def _nonnegative(value: str, line_number: int, name: str, *, positive: bool = False) -> int: + if not value.isascii() or not value.isdigit(): + raise ValueError(f"line {line_number}: {name} must be a canonical nonnegative integer") + number = int(value) + if number >= 1 << 63 or (positive and number == 0): + raise ValueError(f"line {line_number}: {name} is outside the supported bound") + return number + + +def _exam_index(value: str, line_number: int, count: int) -> int: + index = _nonnegative(value, line_number, "exam index") + if index >= count: + raise ValueError(f"line {line_number}: exam index outside declared exam bank") + return index + + +__all__ = [ + "ITCExam", "ITCPeriod", "ITCRoom", "ITCExamInstance", "ITCConflictCore", + "parse_itc2007_exam", +] diff --git a/tests/public/test_exam_repair.py b/tests/public/test_exam_repair.py new file mode 100644 index 0000000..c50f125 --- /dev/null +++ b/tests/public/test_exam_repair.py @@ -0,0 +1,146 @@ +"""Public bounded exam-repair adapter contracts.""" +from __future__ import annotations + +import shutil +import struct +import sys + +import pytest + +from spectra.exam_repair import ExamRepairModel +from spectra.exact import ExactRuntime, build_exact_runtime + + +def path_model() -> ExamRepairModel: + return ExamRepairModel.create( + period_ids=("P0", "P1"), + exam_ids=("A", "B", "C"), + students=((1,), (1, 2), (2,)), + candidate_periods=(("P0", "P1"),) * 3, + ) + + +def test_adapter_builds_deterministic_conflicts_and_named_metadata(): + model = ExamRepairModel.create( + period_ids=("AM", "PM", "EVENING"), + exam_ids=("MATH", "CHEM", "HIST"), + students=((4, 2, 4), (2,), (9,)), + candidate_periods=(("AM", "PM"), ("PM",), ("AM", "EVENING")), + exclusions=(("CHEM", "HIST"),), + ) + assert model.problem.edges == ((0, 1), (1, 2)) + assert tuple(reason.kind for reason in model.conflict_reasons) == ( + "shared_student", "explicit_exclusion" + ) + assert model.conflict_reasons[0].student == 2 + assert model.candidates == (("AM", "PM"), ("PM",), ("AM", "EVENING")) + assert model.info == { + "schema": "spectra.exam_repair.model.v1", + "exams": 3, + "periods": 3, + "conflicts": 2, + "shared_student_conflicts": 1, + "explicit_exclusions": 1, + "enrollments": 4, + "scope": "one/two pre-approved periods; shared students and explicit exclusions only", + } + + +def test_event_intersects_closures_pins_and_allowed_periods(): + model = path_model() + query = model.freeze_event( + closed_periods=("P1",), + fixed=(("B", "P1"),), + allowed=(("C", ("P0",)),), + ) + assert query == ((0, 0b01), (1, 0), (2, 0b01)) + with pytest.raises(ValueError, match="unknown period"): + model.freeze_event(closed_periods=("P2",)) + with pytest.raises(ValueError, match="more than once"): + model.freeze_event(fixed=(("A", "P0"), ("A", "P1"))) + with pytest.raises(ValueError, match="period iterable"): + model.freeze_event(allowed=(("A", 7),)) + + +def test_named_assignment_is_checked_against_event_and_conflicts(): + model = path_model() + query = model.freeze_event(fixed=(("A", "P0"), ("C", "P0"))) + labels = b"\x00\x01\x00" + assert model.assignment_from_labels(labels, query) == ( + ("A", "P0"), ("B", "P1"), ("C", "P0") + ) + assert model.check_assignment({"A": "P0", "B": "P1", "C": "P0"}, query) + assert not model.check_assignment({"A": "P0", "B": "P0", "C": "P1"}, query) + assert not model.check_assignment({"A": "P0", "B": "P1"}, query) + + +def test_portable_proof_translates_to_shared_student_chain(): + model = ExamRepairModel.create( + period_ids=("P0", "P1"), + exam_ids=("A", "B"), + students=((17,), (17,)), + candidate_periods=(("P0", "P1"), ("P0", "P1")), + ) + query = model.freeze_event(fixed=(("A", "P0"), ("B", "P0"))) + # A=P0 implies B=P1; the independently forced B=P0 reaches the opposite. + certificate = struct.pack("<4sIII3I", b"SBP1", 2, 2, 1, 0, 3, 2) + record = model.explain_unsat(query, certificate) + assert record["kind"] == "FORCED_OPPOSITES" + assert record["left"]["nodes"][0]["forced_by_event"] is True + assert record["left"]["steps"] == [{ + "kind": "SHARED_STUDENT_CONFLICT", + "from_exam": "A", + "from_period": "P0", + "to_exam": "B", + "therefore_period": "P1", + "student": 17, + }] + with pytest.raises(ValueError, match="does not prove"): + model.explain_unsat((), certificate) + + +def test_empty_domain_proof_names_the_closed_exam(): + model = path_model() + query = model.freeze_event(closed_periods=("P0", "P1")) + certificate = struct.pack("<4sIIII", b"SBP1", 1, 1, 0, 0) + assert model.explain_unsat(query, certificate) == { + "schema": "spectra.exam_repair.proof.v1", + "kind": "EMPTY_DOMAIN", + "exam": "A", + "original_candidates": ["P0", "P1"], + "remaining_candidates": [], + } + + +def test_adapter_rejects_ambiguous_or_out_of_scope_input_shapes(): + with pytest.raises(ValueError, match="one or two"): + ExamRepairModel.create(("P",), ("A",), ((),), ((),)) + with pytest.raises(ValueError, match="unique"): + ExamRepairModel.create(("P", "P"), (), (), ()) + with pytest.raises(ValueError, match="cannot exclude itself"): + ExamRepairModel.create(("P",), ("A",), ((),), (("P",),), exclusions=(("A", "A"),)) + with pytest.raises(ValueError, match="period identifier count exceeds"): + ExamRepairModel.create((f"P{i}" for i in range(65)), (), (), ()) + + +def _compiler_available() -> bool: + return bool(shutil.which("cl" if sys.platform == "win32" else "g++")) + + +@pytest.mark.skipif(sys.platform not in ("linux", "win32") or not _compiler_available(), + reason="supported native compiler unavailable") +def test_adapter_runs_through_public_native_api(tmp_path): + model = path_model() + runtime = ExactRuntime(build_exact_runtime(tmp_path / "native")) + sat_query = model.freeze_event(fixed=(("A", "P0"), ("C", "P0"))) + unsat_query = model.freeze_event(fixed=(("A", "P0"), ("B", "P0"))) + with runtime.prepare(model.problem, mode="scc") as session: + sat = session.solve(sat_query) + unsat = session.solve(unsat_query) + assert sat.status == "SAT_VERIFIED" + assert model.check_assignment(model.assignment_from_labels(sat.labels, sat_query), sat_query) + assert unsat.status == "UNSAT_VERIFIED" + explained = model.explain_unsat(unsat_query, unsat.proof) + assert explained["kind"] == "FORCED_OPPOSITES" + assert any(step.get("student") == 1 + for side in ("left", "right") for step in explained[side]["steps"]) diff --git a/tests/public/test_itc2007_exam.py b/tests/public/test_itc2007_exam.py new file mode 100644 index 0000000..bdbbf1b --- /dev/null +++ b/tests/public/test_itc2007_exam.py @@ -0,0 +1,85 @@ +"""Strict ITC 2007 parser and bounded conflict-core adapter contracts.""" +from __future__ import annotations + +import pytest + +from spectra.itc2007_exam import parse_itc2007_exam + + +FIXTURE = """[Exams:3] +120, 1, 2 +180, 2, 3 +120, 9 +[Periods:3] +01:01:2030, 09:00:00, 180, 0 +01:01:2030, 14:00:00, 120, 5 +02:01:2030, 09:00:00, 240, 0 +[Rooms:1] +100, 0 +[PeriodHardConstraints] +0, EXCLUSION, 2 +1, AFTER, 0 +[RoomHardConstraints] +2, ROOM_EXCLUSIVE +[InstitutionalWeightings] +TWOINAROW, 30 +""" + + +def test_parser_preserves_complete_constraint_inventory(): + instance = parse_itc2007_exam(FIXTURE) + assert len(instance.exams) == 3 + assert instance.exams[0].students == (1, 2) + assert len(instance.periods) == 3 + assert instance.after == ((1, 0),) + assert instance.exclusions == ((0, 2),) + assert instance.room_exclusive == (2,) + assert dict(instance.ignored_constraint_counts) == { + "after": 1, + "room_exclusive": 1, + "room_assignment": 1, + "period_penalties": 1, + "institutional_weightings": 1, + } + assert len(instance.source_sha256) == 64 + + +def test_conflict_core_requires_explicit_scope_acknowledgement(): + instance = parse_itc2007_exam(FIXTURE) + candidates = ((0, 1), (0, 2), (1, 2)) + with pytest.raises(ValueError, match="would ignore"): + instance.conflict_core(candidates) + core = instance.conflict_core(candidates, acknowledge_ignored_constraints=True) + # Exam 0/1 share student 2; exam 0/2 have explicit EXCLUSION. + assert core.model.problem.edges == ((0, 1), (0, 2)) + assert core.model.candidates[0] == ( + "00:01:01:2030 09:00:00", "01:01:01:2030 14:00:00" + ) + assert core.info["schema"] == "spectra.itc2007.conflict_core.v1" + assert "not a complete ITC 2007 timetable" in core.info["claim_boundary"] + + +def test_duration_check_rejects_unapproved_short_period(): + instance = parse_itc2007_exam(FIXTURE) + # Exam 1 lasts 180 minutes; period 1 lasts only 120. + with pytest.raises(ValueError, match="does not fit"): + instance.conflict_core(((0,), (1,), (2,)), acknowledge_ignored_constraints=True) + + +def test_conflict_core_rejects_noniterable_candidate_rows(): + instance = parse_itc2007_exam(FIXTURE) + with pytest.raises(ValueError, match="period-index iterables"): + instance.conflict_core(((0,), 1, (2,)), acknowledge_ignored_constraints=True) + + +@pytest.mark.parametrize("damage, message", [ + (FIXTURE.replace("[Exams:3]", "[Exams:4]"), "row count differs"), + (FIXTURE.replace("[Periods:3]", "[Unknown:3]"), "unknown ITC section"), + (FIXTURE.replace("120, 1, 2", "120, 1, 1"), "duplicate student"), + (FIXTURE.replace("0, EXCLUSION, 2", "0, OTHER, 2"), "unsupported period relation"), + (FIXTURE.replace("0, EXCLUSION, 2", "0, EXCLUSION, 2\n2, EXCLUSION, 0"), + "duplicate period hard constraint"), +]) +def test_parser_fails_closed_on_malformed_instances(damage, message): + with pytest.raises(ValueError, match=message): + parse_itc2007_exam(damage) From f0430f1204075784d403a8f9e2ca2459761b87e7 Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:56:12 -0400 Subject: [PATCH 13/24] Add cross-platform proof-carrying exam transfer validation --- .github/workflows/itc-exam-transfer.yml | 114 ++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 .github/workflows/itc-exam-transfer.yml diff --git a/.github/workflows/itc-exam-transfer.yml b/.github/workflows/itc-exam-transfer.yml new file mode 100644 index 0000000..4ac558a --- /dev/null +++ b/.github/workflows/itc-exam-transfer.yml @@ -0,0 +1,114 @@ +name: itc-exam-transfer +on: + push: + branches: [research/100-blitz-20261016] + paths: + - '.github/workflows/itc-exam-transfer.yml' + - 'spectra/exact*.py' + - 'spectra/exam_repair.py' + - 'spectra/itc2007_exam.py' + - 'spectra/_native/exact_binary.cpp' + - 'experiments/compiled_query/itc_exam_transfer.py' + - 'experiments/compiled_query/test_itc_exam_transfer.py' + - 'experiments/compiled_query/ITC_EXAM_TRANSFER_PROTOCOL.md' + - 'experiments/compiled_query/itc_exam_suite.py' + - 'experiments/compiled_query/test_itc_exam_suite.py' + - 'experiments/compiled_query/ITC_EXAM_SUITE_PROTOCOL.md' + - 'tests/public/test_exam_repair.py' + - 'tests/public/test_itc2007_exam.py' + workflow_dispatch: +permissions: + contents: read +concurrency: + group: itc-exam-transfer-${{ github.ref }} + cancel-in-progress: false +env: + OMP_NUM_THREADS: '1' + OPENBLAS_NUM_THREADS: '1' +jobs: + transfer: + name: ITC conflict-core transfer (${{ matrix.runner }}, Python ${{ matrix.python }}) + runs-on: ${{ matrix.runner }} + timeout-minutes: 45 + strategy: + fail-fast: false + matrix: + include: + - {runner: ubuntu-24.04, python: '3.11'} + - {runner: ubuntu-24.04, python: '3.13'} + - {runner: ubuntu-24.04-arm, python: '3.13'} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + - name: Install the pinned test runner + run: python -m pip install 'pytest==8.4.2' + - uses: actions/checkout@v4 + with: + repository: davidvanbulck/ITC-2007-CETT + ref: ed2d4c0cf5302b77a8b321cd0380de0bb579d538 + path: upstream/ITC-2007-CETT + persist-credentials: false + - name: Check the public adapter and immutable evidence harness + run: | + set -euo pipefail + python -m pytest \ + tests/public/test_exam_repair.py \ + tests/public/test_itc2007_exam.py \ + experiments/compiled_query/test_itc_exam_transfer.py \ + -q --junitxml=transfer-contracts.xml + - name: Freeze the published instance only after the protocol is committed + run: | + set -euo pipefail + python -m experiments.compiled_query.itc_exam_transfer freeze \ + --root itc-transfer-evidence \ + --instance upstream/ITC-2007-CETT/examtt-instances/Instances/set9.exam \ + --solution upstream/ITC-2007-CETT/InitMethods/SolStore/set9_OBSIFeas_0.sol \ + --license upstream/ITC-2007-CETT/LICENSE \ + --queries 1024 \ + --evaluation-role development | tee freeze.log + - name: Run every exact engine and replay every answer + run: | + set -euo pipefail + python -m experiments.compiled_query.itc_exam_transfer run \ + --root itc-transfer-evidence | tee run.log + python -S -m experiments.compiled_query.itc_exam_transfer audit \ + --root itc-transfer-evidence | tee audit.log + python - <<'PY' + import json + from pathlib import Path + result = json.loads(Path('itc-transfer-evidence/audit.json').read_text()) + assert result['functional_complete'] + assert result['baseline_valid_full_itc_hard_constraints'] + assert result['queries'] == 1024 + assert result['model']['schema'] == 'spectra.itc2007.conflict_core.v1' + assert result['modes']['auto']['auto_policy'] == 'structural-v2' + assert result['modes']['auto']['selected_mode'] == 'raw' + assert result['modes']['auto']['auto_policy_reason'] == 'high_pin_density' + print('Functional transfer complete. This is not a full ITC timetable claim.') + PY + - name: Preserve exact source and environment receipts + if: always() + run: | + set -euo pipefail + mkdir -p transfer-receipts + git rev-parse HEAD > transfer-receipts/source_commit.txt + git rev-parse HEAD^{tree} > transfer-receipts/source_tree.txt + git -C upstream/ITC-2007-CETT rev-parse HEAD > transfer-receipts/upstream_commit.txt + python -VV > transfer-receipts/python.txt + uname -a > transfer-receipts/platform.txt + lscpu > transfer-receipts/cpu.txt + cp transfer-contracts.xml freeze.log run.log audit.log transfer-receipts/ 2>/dev/null || true + - uses: actions/upload-artifact@v4 + if: always() + with: + name: itc-exam-transfer-${{ matrix.runner }}-py${{ matrix.python }}-${{ github.run_id }} + path: | + itc-transfer-evidence/ + transfer-receipts/ + retention-days: 90 + if-no-files-found: warn From a0f5d18ac4651c6ab3a04303fafd724c1b6d5a91 Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:56:23 -0400 Subject: [PATCH 14/24] Add prospective multi-instance ITC holdout suite --- .github/workflows/itc-exam-suite.yml | 127 +++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 .github/workflows/itc-exam-suite.yml diff --git a/.github/workflows/itc-exam-suite.yml b/.github/workflows/itc-exam-suite.yml new file mode 100644 index 0000000..ef6a8bc --- /dev/null +++ b/.github/workflows/itc-exam-suite.yml @@ -0,0 +1,127 @@ +name: itc-exam-suite +on: + push: + branches: [research/100-blitz-20261016] + paths: + - '.github/workflows/itc-exam-suite.yml' + - 'spectra/exact*.py' + - 'spectra/exam_repair.py' + - 'spectra/itc2007_exam.py' + - 'spectra/_native/exact_binary.cpp' + - 'experiments/compiled_query/exact.py' + - 'experiments/compiled_query/exact_native.cpp' + - 'experiments/compiled_query/test_exact.py' + - 'experiments/compiled_query/itc_exam_transfer.py' + - 'experiments/compiled_query/itc_exam_suite.py' + - 'experiments/compiled_query/test_itc_exam_transfer.py' + - 'experiments/compiled_query/test_itc_exam_suite.py' + - 'experiments/compiled_query/ITC_EXAM_TRANSFER_PROTOCOL.md' + - 'experiments/compiled_query/ITC_EXAM_SUITE_PROTOCOL.md' + workflow_dispatch: +permissions: + contents: read +concurrency: + group: itc-exam-suite-${{ github.ref }} + cancel-in-progress: false +env: + OMP_NUM_THREADS: '1' + OPENBLAS_NUM_THREADS: '1' +jobs: + suite: + name: Prospective ITC suite (${{ matrix.runner }}, Python ${{ matrix.python }}) + runs-on: ${{ matrix.runner }} + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + include: + - {runner: ubuntu-24.04, python: '3.11'} + - {runner: ubuntu-24.04, python: '3.13'} + - {runner: ubuntu-24.04-arm, python: '3.13'} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + - run: python -m pip install 'build==1.3.0' 'pytest==8.4.2' + - uses: actions/checkout@v4 + with: + repository: davidvanbulck/ITC-2007-CETT + ref: ed2d4c0cf5302b77a8b321cd0380de0bb579d538 + path: upstream/ITC-2007-CETT + persist-credentials: false + - name: Validate selector and suite contracts + run: | + set -euo pipefail + python -m pytest \ + experiments/compiled_query/test_exact.py \ + experiments/compiled_query/test_itc_exam_transfer.py \ + experiments/compiled_query/test_itc_exam_suite.py \ + tests/public/test_exam_repair.py \ + tests/public/test_itc2007_exam.py \ + -q -k 'auto or tiled_witnesses or itc or exam_repair or suite' \ + --junitxml=suite-contracts.xml + - name: Build and exercise the installed application API + run: | + set -euo pipefail + python -m build --wheel --outdir suite-wheel + python -m venv suite-install + suite-install/bin/python -m pip install --no-deps suite-wheel/*.whl + suite-install/bin/python -I scripts/check_exam_repair_installation.py \ + --out installed-exam-repair.json | tee installed-exam-repair.log + - name: Freeze all roles before any holdout execution + run: | + set -euo pipefail + python -m experiments.compiled_query.itc_exam_suite freeze \ + --root itc-suite-evidence \ + --upstream-root upstream/ITC-2007-CETT \ + --queries 1024 \ + --holdout-max-auto-raw-ratio 1.15 | tee freeze.log + - name: Execute and independently audit every case + run: | + set -euo pipefail + python -m experiments.compiled_query.itc_exam_suite run \ + --root itc-suite-evidence | tee run.log + python -S -m experiments.compiled_query.itc_exam_suite audit \ + --root itc-suite-evidence | tee audit.log + python - <<'PY' + import json + from pathlib import Path + result = json.loads(Path('itc-suite-evidence/suite-audit.json').read_text()) + assert result['functional_complete'] + assert result['holdout_gate_pass'], result['holdout_failures'] + roles = [case['role'] for case in result['cases']] + assert roles == ['development', 'holdout', 'holdout'] + for case in result['cases']: + assert case['queries'] == 1024 + assert case['sat'] + case['unsat'] == 1024 + assert case['auto_vs_raw_paired_median_ratio'] > 0 + print('Prospective holdout guardrail passed. Not a full timetabling claim.') + PY + - name: Preserve exact environment and source receipts + if: always() + run: | + set -euo pipefail + mkdir -p suite-receipts + git rev-parse HEAD > suite-receipts/source_commit.txt + git rev-parse HEAD^{tree} > suite-receipts/source_tree.txt + git -C upstream/ITC-2007-CETT rev-parse HEAD > suite-receipts/upstream_commit.txt + python -VV > suite-receipts/python.txt + uname -a > suite-receipts/platform.txt + lscpu > suite-receipts/cpu.txt + cp suite-contracts.xml freeze.log run.log audit.log \ + installed-exam-repair.json installed-exam-repair.log \ + suite-receipts/ 2>/dev/null || true + - uses: actions/upload-artifact@v4 + if: always() + with: + name: itc-exam-suite-${{ matrix.runner }}-py${{ matrix.python }}-${{ github.run_id }} + path: | + itc-suite-evidence/ + suite-receipts/ + suite-wheel/ + retention-days: 90 + if-no-files-found: warn From 9b14a35d7744896b8b86b4014d825b9f082d8294 Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:56:38 -0400 Subject: [PATCH 15/24] Link the proof-carrying exam-repair application guide --- docs/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/README.md b/docs/README.md index f11fd18..96d2374 100644 --- a/docs/README.md +++ b/docs/README.md @@ -24,6 +24,7 @@ historical neural, MCTS, low-bit, general-SAT, or universal CPU-performance clai | Install and run the public tools | [Quick start](../README.md#base-toolkit-quick-start) | | Understand API, input formats and return values | [API and CLI](API.md) | | Build and use checked SAT/UNSAT exact queries | [Proof-carrying exact API](EXACT_API.md) | +| Apply the exact core to bounded exam rescheduling | [Proof-carrying exam repair](EXAM_REPAIR.md) | | Check released, merged, experimental and failed work | [Current status](STATUS.md) | | Run tests, build a wheel or reproduce experiments | [Development](DEVELOPMENT.md) | | Prepare a package release | [Release checklist](RELEASING.md) | From 76ba450275a3b4ebba0fc1eef715fe97a90ed44b Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:56:54 -0400 Subject: [PATCH 16/24] Extend installed-package validation to exam repair --- .github/workflows/public-package.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/public-package.yml b/.github/workflows/public-package.yml index 4efdf81..9b60760 100644 --- a/.github/workflows/public-package.yml +++ b/.github/workflows/public-package.yml @@ -1,7 +1,7 @@ name: public-package on: pull_request: - branches: [main, "maintenance/**", "research/deductive-search-*", "research/focused-search-*", "research/research-engineering-*", "research/faster-queries-review-*"] + branches: [main, "maintenance/**", "research/deductive-search-*", "research/focused-search-*", "research/research-engineering-*", "research/faster-queries-review-*", "research/exact-public-api-*"] workflow_dispatch: permissions: contents: read @@ -48,6 +48,7 @@ jobs: .public-package/install/venv/bin/python -I scripts/check_focused_installation.py | tee .public-package/focused-install.txt .public-package/install/venv/bin/python -I scripts/check_dpll_installation.py | tee .public-package/dpll-install.txt .public-package/install/venv/bin/python -I scripts/check_exact_installation.py --out .public-package/exact-install.json | tee .public-package/exact-install.txt + .public-package/install/venv/bin/python -I scripts/check_exam_repair_installation.py --out .public-package/exam-repair-install.json | tee .public-package/exam-repair-install.txt - name: Run and replay frozen compact-state comparison run: | set -o pipefail From 6390470d5b26b77c13a932e2c5e74576bb4a1a7e Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:57:02 -0400 Subject: [PATCH 17/24] Remove one-shot blitz publication workflow --- .github/workflows/publish-100-blitz.yml | 81 ------------------------- 1 file changed, 81 deletions(-) delete mode 100644 .github/workflows/publish-100-blitz.yml diff --git a/.github/workflows/publish-100-blitz.yml b/.github/workflows/publish-100-blitz.yml deleted file mode 100644 index f2d5dd8..0000000 --- a/.github/workflows/publish-100-blitz.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: publish-100-blitz -on: - push: - branches: [research/100-blitz-20261016] - paths: ['.github/workflows/publish-100-blitz.yml'] -permissions: - contents: write -concurrency: - group: publish-100-blitz - cancel-in-progress: false -jobs: - publish: - runs-on: ubuntu-24.04 - timeout-minutes: 10 - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Verify and reconstruct reviewed patch - env: - EXPECTED_B64: f75650c04f59e0ae6e02d2190ebd56668442d790276d5cc9fa3dc11d188108a7 - EXPECTED_XZ: 31780857b8f024c4cb4f1144d04f8e8fdebf8b902ab1acd7c293673aacaef036 - EXPECTED_PATCH: dcd47a5a9a621f9f20d91cf040cc125c5300089b699dbacfbd25a2be84e83ab6 - run: | - set -euo pipefail - mkdir -p /tmp/blitz - cat .delivery/100-blitz-source/{00,01,02,03,04,05,06,07,08,09} > /tmp/blitz/patch.xz.b64 - test "$(sha256sum /tmp/blitz/patch.xz.b64 | cut -d' ' -f1)" = "$EXPECTED_B64" - base64 --decode /tmp/blitz/patch.xz.b64 > /tmp/blitz/patch.xz - test "$(sha256sum /tmp/blitz/patch.xz | cut -d' ' -f1)" = "$EXPECTED_XZ" - xz --decompress --stdout /tmp/blitz/patch.xz > /tmp/blitz/reviewed.patch - test "$(sha256sum /tmp/blitz/reviewed.patch | cut -d' ' -f1)" = "$EXPECTED_PATCH" - - name: Validate base identities and publish atomically - run: | - set -euo pipefail - test -z "$(git status --porcelain)" - test "$(git rev-parse HEAD:spectra/exact.py)" = 983d33551e29d13016e36cd6038d02d270575b66 - test "$(git rev-parse HEAD:spectra/_native/exact_binary.cpp)" = 96182ee6c7754d24e1c8bc46181dd69a5732d32f - test "$(git rev-parse HEAD:experiments/compiled_query/exact.py)" = 780a7ff1d8f293450fb592ae9e29e129e92a551c - test "$(git rev-parse HEAD:experiments/compiled_query/exact_native.cpp)" = 96182ee6c7754d24e1c8bc46181dd69a5732d32f - git apply --check --index /tmp/blitz/reviewed.patch - git apply --index /tmp/blitz/reviewed.patch - git diff --cached --name-only -z > /tmp/blitz/changed.paths - python - <<'PY' - from pathlib import Path - expected = { - 'docs/EXAM_REPAIR.md', - 'experiments/compiled_query/ITC_EXAM_SUITE_PROTOCOL.md', - 'experiments/compiled_query/ITC_EXAM_TRANSFER_PROTOCOL.md', - 'experiments/compiled_query/exact.py', - 'experiments/compiled_query/exact_native.cpp', - 'experiments/compiled_query/itc_exam_suite.py', - 'experiments/compiled_query/itc_exam_transfer.py', - 'experiments/compiled_query/test_exact.py', - 'experiments/compiled_query/test_itc_exam_suite.py', - 'experiments/compiled_query/test_itc_exam_transfer.py', - 'scripts/check_exam_repair_installation.py', - 'spectra/_native/exact_binary.cpp', - 'spectra/exact.py', - 'spectra/exam_repair.py', - 'spectra/itc2007_exam.py', - 'tests/public/test_exam_repair.py', - 'tests/public/test_itc2007_exam.py', - } - changed = { - item.decode('utf-8') - for item in Path('/tmp/blitz/changed.paths').read_bytes().split(b'\0') - if item - } - if changed != expected: - raise RuntimeError(f'changed path set differs: {sorted(changed ^ expected)}') - PY - git rm -r .delivery/100-blitz-source - if git diff --cached --name-only | grep -q '^\.github/workflows/'; then - echo 'Workflow changes are not permitted in the reconstructed code commit.' >&2 - exit 1 - fi - git config user.name 'Tugrap' - git config user.email '106766396+tugrapaydiner@users.noreply.github.com' - git commit -m 'Publish proof-carrying exam repair and prospective ITC suite' - git push origin HEAD:research/100-blitz-20261016 From 5b7ce2de817a855bf2bce39c32ba88d0f4149a5b Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:58:00 -0400 Subject: [PATCH 18/24] Isolate dependency-light public exam contracts in CI --- .github/workflows/itc-exam-transfer.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/itc-exam-transfer.yml b/.github/workflows/itc-exam-transfer.yml index 4ac558a..1059345 100644 --- a/.github/workflows/itc-exam-transfer.yml +++ b/.github/workflows/itc-exam-transfer.yml @@ -59,8 +59,11 @@ jobs: python -m pytest \ tests/public/test_exam_repair.py \ tests/public/test_itc2007_exam.py \ + --confcutdir=tests/public -q \ + --junitxml=transfer-public-contracts.xml + python -m pytest \ experiments/compiled_query/test_itc_exam_transfer.py \ - -q --junitxml=transfer-contracts.xml + -q --junitxml=transfer-research-contracts.xml - name: Freeze the published instance only after the protocol is committed run: | set -euo pipefail @@ -102,7 +105,8 @@ jobs: python -VV > transfer-receipts/python.txt uname -a > transfer-receipts/platform.txt lscpu > transfer-receipts/cpu.txt - cp transfer-contracts.xml freeze.log run.log audit.log transfer-receipts/ 2>/dev/null || true + cp transfer-public-contracts.xml transfer-research-contracts.xml \ + freeze.log run.log audit.log transfer-receipts/ 2>/dev/null || true - uses: actions/upload-artifact@v4 if: always() with: From ff77d802e9a2bb3c1d97c5acf70d8871dc114f7c Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:58:13 -0400 Subject: [PATCH 19/24] Isolate public and research contracts before ITC holdouts --- .github/workflows/itc-exam-suite.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/itc-exam-suite.yml b/.github/workflows/itc-exam-suite.yml index ef6a8bc..6fa53ff 100644 --- a/.github/workflows/itc-exam-suite.yml +++ b/.github/workflows/itc-exam-suite.yml @@ -56,14 +56,17 @@ jobs: - name: Validate selector and suite contracts run: | set -euo pipefail + python -m pytest \ + tests/public/test_exam_repair.py \ + tests/public/test_itc2007_exam.py \ + --confcutdir=tests/public -q \ + --junitxml=suite-public-contracts.xml python -m pytest \ experiments/compiled_query/test_exact.py \ experiments/compiled_query/test_itc_exam_transfer.py \ experiments/compiled_query/test_itc_exam_suite.py \ - tests/public/test_exam_repair.py \ - tests/public/test_itc2007_exam.py \ - -q -k 'auto or tiled_witnesses or itc or exam_repair or suite' \ - --junitxml=suite-contracts.xml + -q -k 'auto or tiled_witnesses or itc or suite' \ + --junitxml=suite-research-contracts.xml - name: Build and exercise the installed application API run: | set -euo pipefail @@ -112,9 +115,9 @@ jobs: python -VV > suite-receipts/python.txt uname -a > suite-receipts/platform.txt lscpu > suite-receipts/cpu.txt - cp suite-contracts.xml freeze.log run.log audit.log \ - installed-exam-repair.json installed-exam-repair.log \ - suite-receipts/ 2>/dev/null || true + cp suite-public-contracts.xml suite-research-contracts.xml \ + freeze.log run.log audit.log installed-exam-repair.json \ + installed-exam-repair.log suite-receipts/ 2>/dev/null || true - uses: actions/upload-artifact@v4 if: always() with: From 50a8e92c048c1e2b09f82ba8ea65dfd1c295acd8 Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:59:19 -0400 Subject: [PATCH 20/24] Add an independent ITC exam-repair replication guide --- .../compiled_query/ITC_EXAM_REPLICATION.md | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 experiments/compiled_query/ITC_EXAM_REPLICATION.md diff --git a/experiments/compiled_query/ITC_EXAM_REPLICATION.md b/experiments/compiled_query/ITC_EXAM_REPLICATION.md new file mode 100644 index 0000000..3890d53 --- /dev/null +++ b/experiments/compiled_query/ITC_EXAM_REPLICATION.md @@ -0,0 +1,116 @@ +# Independent replication: proof-carrying ITC exam repair + +This guide is for an outside runner who has not changed the selector, event bank, +acceptance threshold, or result interpretation. A successful run reproduces a +bounded exam-repair transfer. It does **not** establish full ITC-2007 timetabling, +operator adoption, universal performance, or algorithmic priority. + +## 1. Pin both repositories + +Record the exact SPECTRA commit under review and use the fixed upstream revision: + +```bash +git clone https://github.com/tugrapaydiner/SPECTRA.git +cd SPECTRA +git checkout + +git clone https://github.com/davidvanbulck/ITC-2007-CETT.git upstream/ITC-2007-CETT +git -C upstream/ITC-2007-CETT checkout ed2d4c0cf5302b77a8b321cd0380de0bb579d538 +``` + +Do not substitute newer upstream bytes. Preserve the upstream `LICENSE` in every +shared evidence package. + +## 2. Record the environment before execution + +```bash +mkdir -p replication-receipts +git rev-parse HEAD > replication-receipts/spectra-commit.txt +git rev-parse HEAD^{tree} > replication-receipts/spectra-tree.txt +git -C upstream/ITC-2007-CETT rev-parse HEAD > replication-receipts/upstream-commit.txt +python -VV > replication-receipts/python.txt +uname -a > replication-receipts/platform.txt +lscpu > replication-receipts/cpu.txt 2>/dev/null || true +c++ --version > replication-receipts/compiler.txt 2>&1 +``` + +Use a GIL-enabled CPython 3.11 or 3.13 and a C++17 compiler. Do not install or load +an alternative solver for the measured calls. + +## 3. Check source and installed-package contracts + +```bash +python -m pip install 'build==1.3.0' 'pytest==8.4.2' +python -m pytest \ + tests/public/test_exam_repair.py \ + tests/public/test_itc2007_exam.py \ + --confcutdir=tests/public -q +python -m pytest \ + experiments/compiled_query/test_exact.py \ + experiments/compiled_query/test_itc_exam_transfer.py \ + experiments/compiled_query/test_itc_exam_suite.py \ + -q -k 'auto or tiled_witnesses or itc or suite' + +python -m build --wheel --outdir replication-wheel +python -m venv replication-install +replication-install/bin/python -m pip install --no-deps replication-wheel/*.whl +replication-install/bin/python -I scripts/check_exam_repair_installation.py \ + --out replication-receipts/installed-exam-repair.json +``` + +On Windows, use `replication-install/Scripts/python.exe` and a configured MSVC x64 +developer environment. + +## 4. Freeze before running any case + +The freeze must complete before inspecting any holdout answer or timing: + +```bash +python -m experiments.compiled_query.itc_exam_suite freeze \ + --root itc-suite-evidence \ + --upstream-root upstream/ITC-2007-CETT \ + --queries 1024 \ + --holdout-max-auto-raw-ratio 1.15 +``` + +Confirm that `suite-manifest.json` records, in order: + +1. `set9-development` with role `development`; +2. `set10-holdout` with role `holdout`; +3. `set12-holdout` with role `holdout`. + +Do not change the roles, query count, five-repetition schedule, selector-v2 rule or +1.15 paired-median guardrail after this point. + +## 5. Execute once and audit without native code + +```bash +python -m experiments.compiled_query.itc_exam_suite run \ + --root itc-suite-evidence +python -S -m experiments.compiled_query.itc_exam_suite audit \ + --root itc-suite-evidence +``` + +The audit must independently regenerate every derived input, replay every `SCQ2` +answer from the original `SBC1` packets, require cross-engine decision agreement, +and publish all holdout failures. Do not delete negative cells or repeat a failed +measurement under a changed rule. + +## 6. Report every result + +Publish the complete `itc-suite-evidence` directory plus the receipts above. At a +minimum, report: + +- SPECTRA commit and tree; +- upstream commit; +- OS, CPU, Python and compiler; +- all source/public/installed contract outcomes; +- each case's role, exam count, conflict count, query count, SAT/UNSAT counts, + selected automatic engine and policy reason; +- all five paired `auto_elapsed / raw_elapsed` ratios per holdout; +- paired-median ratio and pass/fail against 1.15; +- every execution or audit failure, including resource failures. + +A useful independent report explicitly says whether the bytes and conclusions +match the project-owned CI artifact. A disagreement is a valid replication result +and should not be hidden or relabelled as an environment problem without evidence. From d294631d8063438d42e318b5167fc6e3808eff9f Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:59:30 -0400 Subject: [PATCH 21/24] Link the independent exam-repair replication procedure --- docs/EXAM_REPAIR.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/EXAM_REPAIR.md b/docs/EXAM_REPAIR.md index 304b0b3..340333b 100644 --- a/docs/EXAM_REPAIR.md +++ b/docs/EXAM_REPAIR.md @@ -60,3 +60,8 @@ The suite rotates measured engine order, preserves every failed cell, and gates only the predeclared auto/raw overhead on holdout cases. Passing that suite is application-transfer evidence, not an operator deployment or full ITC-2007 optimization result. + +An outside runner can reproduce the source, installed-wheel, freeze, execution and +offline-audit boundaries using the +[independent replication guide](../experiments/compiled_query/ITC_EXAM_REPLICATION.md). +Negative or conflicting replications are explicitly valid results. From ed011fc2280b1da05a6e0fc8ecfd7639cf2a735e Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 15:03:38 -0400 Subject: [PATCH 22/24] Publish prospective multi-instance ITC exam-repair results --- .../ITC_EXAM_SUITE_RESULTS_20261016.md | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 experiments/compiled_query/ITC_EXAM_SUITE_RESULTS_20261016.md diff --git a/experiments/compiled_query/ITC_EXAM_SUITE_RESULTS_20261016.md b/experiments/compiled_query/ITC_EXAM_SUITE_RESULTS_20261016.md new file mode 100644 index 0000000..592fa15 --- /dev/null +++ b/experiments/compiled_query/ITC_EXAM_SUITE_RESULTS_20261016.md @@ -0,0 +1,164 @@ +# Prospective ITC exam-repair suite — 2026-10-16 + +## Result + +The prospectively fixed bounded exam-repair suite completed on all three declared +environments. Both execution holdouts passed the unchanged paired-median +automatic/raw guardrail of **1.15**. Every admitted result was independently +replayed from the original pinned ITC bytes, and no failed cell or output was +removed. + +This is application-transfer evidence for the binary-domain exam-conflict core. It +is **not** a full ITC-2007 timetabling result, an operator deployment, outside-team +replication, universal performance evidence, or an algorithmic-priority claim. + +## Frozen boundary + +| Item | Value | +|---|---| +| Measured SPECTRA commit | `ff77d802e9a2bb3c1d97c5acf70d8871dc114f7c` | +| Published implementation commit | `3036a3c97cf5706d60ab9e277708081b4f241954` | +| Upstream ITC repository commit | `ed2d4c0cf5302b77a8b321cd0380de0bb579d538` | +| Selector policy | `structural-v2` | +| Requests per case | 1,024 | +| Warmup | one untimed call per mode | +| Measured repetitions | five | +| Mode order | rotated each repetition | +| Holdout gate | paired median of `auto_elapsed / raw_elapsed <= 1.15` | +| Development case | `set9` | +| Execution holdouts | `set10`, `set12` | + +The implementation, policy, case roles, event generator, repetition count and gate +were committed before any holdout execution. The automatic engine used packet +structure only. All three cases triggered `high_pin_density` and selected `raw`. + +## Functional outcomes + +The decisions and counts were identical on x86 Python 3.11, x86 Python 3.13 and +native ARM64 Python 3.13. + +| Case | Role | Exams | Periods | Enrolments | Conflicts | Explicit exclusions | SAT | UNSAT | Pins | +|---|---|---:|---:|---:|---:|---:|---:|---:|---:| +| `set9` | development | 169 | 25 | 2,532 | 1,114 | 1 | 268 | 756 | 9,698 | +| `set10` | holdout | 214 | 32 | 7,853 | 1,133 | 0 | 327 | 697 | 9,311 | +| `set12` | holdout | 78 | 12 | 3,685 | 560 | 6 | 24 | 1,000 | 10,361 | + +For `set10`, 178 UNSAT answers used an empty-domain proof and 519 used a +forced-opposites path proof. For `set12`, those counts were 269 and 731. The native +engines agreed on every SAT/UNSAT decision, and the standard-library audit checked +every complete assignment or contradiction proof against the original fixed graph +and current restrictions. + +## Prospective guardrail + +Values below are the median of the five paired repetition ratios, not a ratio of +independently aggregated medians. + +| Environment | `set9` development | `set10` holdout | `set12` holdout | Holdout gate | +|---|---:|---:|---:|---| +| Linux x86, Python 3.11 | 0.995442 | **0.999355** | **1.003860** | PASS | +| Linux x86, Python 3.13 | 1.073799 | **1.019084** | **1.026623** | PASS | +| Linux ARM64, Python 3.13 | 1.017422 | **0.999036** | **1.000705** | PASS | + +The largest holdout paired-median ratio was **1.026623**, leaving approximately +10.7 percentage points of margin to the fixed 1.15 limit. + +Individual repetitions were intentionally retained. In particular, the first +`set12` repetition on x86 Python 3.13 had ratio **1.168387**, and the first +`set10` repetition on x86 Python 3.11 had ratio **1.158769**. Both exceed the gate +when viewed alone; the predeclared gate is the paired median over five rotating +repetitions, so these are negative retained observations rather than deleted or +retried cells. + +### Paired ratios, all repetitions + +| Environment | Case | Five `auto/raw` ratios | +|---|---|---| +| x86 / Python 3.11 | `set10` | 1.158769, 0.999355, 1.000486, 0.984425, 0.995438 | +| x86 / Python 3.11 | `set12` | 1.123582, 1.004558, 1.000192, 1.003860, 0.997922 | +| x86 / Python 3.13 | `set10` | 1.098272, 1.066995, 1.018905, 0.998810, 1.019084 | +| x86 / Python 3.13 | `set12` | 1.168387, 1.012663, 1.026623, 0.978225, 1.040015 | +| ARM64 / Python 3.13 | `set10` | 1.112409, 0.999036, 0.987604, 1.004978, 0.985442 | +| ARM64 / Python 3.13 | `set12` | 1.099958, 0.985422, 0.994436, 1.000705, 1.030142 | + +The automatic and explicit raw modes execute the same native engine after policy +selection. Ratios close to one therefore measure selection/dispatch noise and host +variation rather than an algorithmic speedup. The purpose of this gate is to stop +the automatic policy from imposing material overhead on unseen application cases. + +## Complete-call medians + +Elapsed time includes native construction, all 1,024 decisions, complete witnesses +or portable proofs, independent native checking, `SCQ2` construction, disposal and +hashing. Candidate generation, packet creation, compilation and process startup are +outside the measured call. + +| Environment | Case | Auto | Raw | SCC | Route | +|---|---|---:|---:|---:|---:| +| x86 / Python 3.11 | `set9` | 1.286 ms | 1.292 ms | 1.288 ms | 1.657 ms | +| x86 / Python 3.11 | `set10` | 1.163 ms | 1.161 ms | 1.144 ms | 1.358 ms | +| x86 / Python 3.11 | `set12` | 0.888 ms | 0.887 ms | 0.987 ms | 1.199 ms | +| x86 / Python 3.13 | `set9` | 0.823 ms | 0.765 ms | 0.788 ms | 0.952 ms | +| x86 / Python 3.13 | `set10` | 0.697 ms | 0.666 ms | 0.693 ms | 0.795 ms | +| x86 / Python 3.13 | `set12` | 0.536 ms | 0.523 ms | 0.605 ms | 0.725 ms | +| ARM64 / Python 3.13 | `set9` | 1.114 ms | 1.095 ms | 1.052 ms | 1.402 ms | +| ARM64 / Python 3.13 | `set10` | 1.004 ms | 1.004 ms | 0.972 ms | 1.172 ms | +| ARM64 / Python 3.13 | `set12` | 0.772 ms | 0.771 ms | 0.824 ms | 1.050 ms | + +SCC remains slightly faster than raw on some cells. Selector-v2 deliberately +prioritises a conservative no-material-overhead rule for heavily pinned application +banks rather than claiming a per-cell optimum. The development result does not +justify retuning after seeing the holdouts. + +## CI and artifacts + +### Development transfer + +Workflow run: `38951373279` + +- Linux x86 / Python 3.11: PASS +- Linux x86 / Python 3.13: PASS +- Native Linux ARM64 / Python 3.13: PASS + +Artifacts: + +| Environment | Artifact ID | Artifact SHA-256 | +|---|---:|---| +| x86 / Python 3.11 | `12068467826` | `3afc63b70f8499c03fe97df78eff288c8316accaf714bace3585be938cab1fe4` | +| x86 / Python 3.13 | `12068077883` | `32823ece7589b6f76901469c141210ca3796d489f8a90eaa29aaac0e933d69d1` | +| ARM64 / Python 3.13 | `12068222627` | `d5de0a96b0a8347df58296e6bad01e4cd24e49cb908e952c47056609f2acf762` | + +### Prospective suite + +Workflow run: `38951398495` + +- Linux x86 / Python 3.11: PASS +- Linux x86 / Python 3.13: PASS +- Native Linux ARM64 / Python 3.13: PASS +- Source contracts: PASS on all cells +- Installed-wheel exam-repair execution: PASS on all cells +- Freeze-before-execution: PASS on all cells +- Independent source-only audit: PASS on all cells + +Artifacts: + +| Environment | Artifact ID | Artifact SHA-256 | +|---|---:|---| +| x86 / Python 3.11 | `12068498169` | `fb100a67983b02c7969809efb680deaf5b4002c6f2242122d8cca8ff4a415c23` | +| x86 / Python 3.13 | `12068552843` | `8f86935fa6e6811c56fc4156d8dc9ea422c4a14b5ef0cba356b496d3ef4208e7` | +| ARM64 / Python 3.13 | `12068023095` | `35247cef687fc26ccbfec69d6903ed421a4a3fc9df8682b7143afa0600b055f4` | + +## Preserved failed attempts + +The first transfer run (`38951148893`) and first suite run (`38951172493`) failed +during test collection because the dependency-light public-test command loaded the +repository-wide ML `tests/conftest.py` without NumPy. They stopped before evidence +freezing or any holdout execution. The correction isolated public tests with +`--confcutdir=tests/public`; it did not change the selector, cases, event bank, +repetitions, timing order or guardrail. The failed runs remain visible. + +## Replication + +Follow [`ITC_EXAM_REPLICATION.md`](ITC_EXAM_REPLICATION.md) from a clean checkout. +An independent disagreement or negative result is valid evidence and should be +published with the complete artifacts rather than normalised away. From 008e6d68290e96706b586b8a33634f507f73a66d Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 15:03:45 -0400 Subject: [PATCH 23/24] Link the completed prospective ITC suite results --- docs/EXAM_REPAIR.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/EXAM_REPAIR.md b/docs/EXAM_REPAIR.md index 340333b..812b150 100644 --- a/docs/EXAM_REPAIR.md +++ b/docs/EXAM_REPAIR.md @@ -56,6 +56,9 @@ The research transfer protocol is documented in The selector-v2 policy and its `set10`/`set12` execution holdouts are fixed in [`ITC_EXAM_SUITE_PROTOCOL.md`](../experiments/compiled_query/ITC_EXAM_SUITE_PROTOCOL.md). +The completed cross-platform measurements, retained negative repetitions and +artifact digests are published in the +[`2026-10-16 suite results`](../experiments/compiled_query/ITC_EXAM_SUITE_RESULTS_20261016.md). The suite rotates measured engine order, preserves every failed cell, and gates only the predeclared auto/raw overhead on holdout cases. Passing that suite is application-transfer evidence, not an operator deployment or full ITC-2007 From 13d1d4192e97a4e372ac4aac7743a88b55e814bc Mon Sep 17 00:00:00 2001 From: Tugrap <106766396+tugrapaydiner@users.noreply.github.com> Date: Fri, 16 Oct 2026 15:05:34 -0400 Subject: [PATCH 24/24] Add self-verifying recovery artifacts for the 100-point blitz checkpoint --- .github/workflows/checkpoint-recovery.yml | 59 +++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/workflows/checkpoint-recovery.yml diff --git a/.github/workflows/checkpoint-recovery.yml b/.github/workflows/checkpoint-recovery.yml new file mode 100644 index 0000000..20e9302 --- /dev/null +++ b/.github/workflows/checkpoint-recovery.yml @@ -0,0 +1,59 @@ +name: checkpoint-recovery +on: + push: + branches: [research/100-blitz-20261016] + paths: ['.github/workflows/checkpoint-recovery.yml'] + workflow_dispatch: +permissions: + contents: read +concurrency: + group: checkpoint-recovery-${{ github.ref }} + cancel-in-progress: false +jobs: + recover: + runs-on: ubuntu-24.04 + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + - name: Verify repository and create exact recovery materials + env: + REVIEW_BASE: 575eb1e657fc8c5d06fd56a519efd59f95dc0a2d + run: | + set -euo pipefail + mkdir -p checkpoint-recovery + git merge-base --is-ancestor "$REVIEW_BASE" HEAD + git diff --check "$REVIEW_BASE"..HEAD + git fsck --strict + git rev-parse HEAD > checkpoint-recovery/source_commit.txt + git rev-parse HEAD^{tree} > checkpoint-recovery/source_tree.txt + printf '%s\n' "$REVIEW_BASE" > checkpoint-recovery/review_base.txt + git log --format='%H %T %s' "$REVIEW_BASE"..HEAD > checkpoint-recovery/commit_ledger.txt + git diff --binary "$REVIEW_BASE"..HEAD > checkpoint-recovery/review.patch + git format-patch --stdout --binary "$REVIEW_BASE"..HEAD > checkpoint-recovery/review-series.patch + git archive --format=tar.gz --output=checkpoint-recovery/source.tar.gz HEAD + git bundle create checkpoint-recovery/source.bundle HEAD + git bundle verify checkpoint-recovery/source.bundle > checkpoint-recovery/bundle-verify.txt 2>&1 + python - <<'PY' + import hashlib, json + from pathlib import Path + root = Path('checkpoint-recovery') + files = {} + for path in sorted(root.iterdir()): + if path.is_file() and path.name != 'SHA256SUMS.json': + files[path.name] = { + 'bytes': path.stat().st_size, + 'sha256': hashlib.sha256(path.read_bytes()).hexdigest(), + } + (root / 'SHA256SUMS.json').write_text( + json.dumps(files, indent=2, sort_keys=True) + '\n', encoding='utf-8') + PY + - uses: actions/upload-artifact@v4 + with: + name: spectra-100-blitz-recovery-${{ github.sha }} + path: checkpoint-recovery/ + include-hidden-files: true + retention-days: 90 + if-no-files-found: error