├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── quantumchess.net.unneeded-measure.png ├── screenshot.png └── src ├── Chess_bdt45.svg ├── Chess_blt45.svg ├── Chess_kdt45.svg ├── Chess_klt45.svg ├── Chess_ndt45.svg ├── Chess_nlt45.svg ├── Chess_pdt45.svg ├── Chess_plt45.svg ├── Chess_qdt45.svg ├── Chess_qlt45.svg ├── Chess_rdt45.svg ├── Chess_rlt45.svg ├── chess.rs ├── druid.rs ├── gates.rs ├── main.rs ├── qcomp.rs ├── qcomp_explainable.rs └── term.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "adler" 5 | version = "0.2.3" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" 8 | 9 | [[package]] 10 | name = "aljabar" 11 | version = "1.0.2" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "3577221f47e6d104ddb6dd9748918a8ac0d8a45b491938f9de121852aaf9666d" 14 | dependencies = [ 15 | "paste", 16 | "smallvec 0.6.13", 17 | ] 18 | 19 | [[package]] 20 | name = "anyhow" 21 | version = "1.0.37" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "ee67c11feeac938fae061b232e38e0b6d94f97a9df10e6271319325ac4c56a86" 24 | 25 | [[package]] 26 | name = "arrayvec" 27 | version = "0.5.2" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 30 | 31 | [[package]] 32 | name = "associative-cache" 33 | version = "1.0.1" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "46016233fc1bb55c23b856fe556b7db6ccd05119a0a392e04f0b3b7c79058f16" 36 | 37 | [[package]] 38 | name = "atk" 39 | version = "0.9.0" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "812b4911e210bd51b24596244523c856ca749e6223c50a7fbbba3f89ee37c426" 42 | dependencies = [ 43 | "atk-sys", 44 | "bitflags", 45 | "glib", 46 | "glib-sys", 47 | "gobject-sys", 48 | "libc", 49 | ] 50 | 51 | [[package]] 52 | name = "atk-sys" 53 | version = "0.10.0" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "f530e4af131d94cc4fa15c5c9d0348f0ef28bac64ba660b6b2a1cf2605dedfce" 56 | dependencies = [ 57 | "glib-sys", 58 | "gobject-sys", 59 | "libc", 60 | "system-deps", 61 | ] 62 | 63 | [[package]] 64 | name = "atty" 65 | version = "0.2.14" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 68 | dependencies = [ 69 | "hermit-abi", 70 | "libc", 71 | "winapi", 72 | ] 73 | 74 | [[package]] 75 | name = "autocfg" 76 | version = "1.0.1" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 79 | 80 | [[package]] 81 | name = "base-x" 82 | version = "0.2.8" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" 85 | 86 | [[package]] 87 | name = "base64" 88 | version = "0.12.3" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" 91 | 92 | [[package]] 93 | name = "bitflags" 94 | version = "1.2.1" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 97 | 98 | [[package]] 99 | name = "bitmaps" 100 | version = "2.1.0" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" 103 | dependencies = [ 104 | "typenum", 105 | ] 106 | 107 | [[package]] 108 | name = "block" 109 | version = "0.1.6" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 112 | 113 | [[package]] 114 | name = "bumpalo" 115 | version = "3.4.0" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" 118 | 119 | [[package]] 120 | name = "byteorder" 121 | version = "1.3.4" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 124 | 125 | [[package]] 126 | name = "cairo-rs" 127 | version = "0.9.1" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "c5c0f2e047e8ca53d0ff249c54ae047931d7a6ebe05d00af73e0ffeb6e34bdb8" 130 | dependencies = [ 131 | "bitflags", 132 | "cairo-sys-rs", 133 | "glib", 134 | "glib-sys", 135 | "gobject-sys", 136 | "libc", 137 | "thiserror", 138 | ] 139 | 140 | [[package]] 141 | name = "cairo-sys-rs" 142 | version = "0.10.0" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "2ed2639b9ad5f1d6efa76de95558e11339e7318426d84ac4890b86c03e828ca7" 145 | dependencies = [ 146 | "glib-sys", 147 | "libc", 148 | "system-deps", 149 | ] 150 | 151 | [[package]] 152 | name = "cc" 153 | version = "1.0.66" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" 156 | 157 | [[package]] 158 | name = "cfg-if" 159 | version = "0.1.10" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 162 | 163 | [[package]] 164 | name = "cfg-if" 165 | version = "1.0.0" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 168 | 169 | [[package]] 170 | name = "cmake" 171 | version = "0.1.45" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855" 174 | dependencies = [ 175 | "cc", 176 | ] 177 | 178 | [[package]] 179 | name = "cocoa" 180 | version = "0.23.0" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "c54201c07dcf3a5ca33fececb8042aed767ee4bfd5a0235a8ceabcda956044b2" 183 | dependencies = [ 184 | "bitflags", 185 | "block", 186 | "cocoa-foundation", 187 | "core-foundation 0.9.1", 188 | "core-graphics 0.22.2", 189 | "foreign-types", 190 | "libc", 191 | "objc", 192 | ] 193 | 194 | [[package]] 195 | name = "cocoa-foundation" 196 | version = "0.1.0" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" 199 | dependencies = [ 200 | "bitflags", 201 | "block", 202 | "core-foundation 0.9.1", 203 | "core-graphics-types", 204 | "foreign-types", 205 | "libc", 206 | "objc", 207 | ] 208 | 209 | [[package]] 210 | name = "console_log" 211 | version = "0.2.0" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494" 214 | dependencies = [ 215 | "log", 216 | "web-sys", 217 | ] 218 | 219 | [[package]] 220 | name = "const_fn" 221 | version = "0.4.4" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" 224 | 225 | [[package]] 226 | name = "core-foundation" 227 | version = "0.7.0" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" 230 | dependencies = [ 231 | "core-foundation-sys 0.7.0", 232 | "libc", 233 | ] 234 | 235 | [[package]] 236 | name = "core-foundation" 237 | version = "0.9.1" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" 240 | dependencies = [ 241 | "core-foundation-sys 0.8.2", 242 | "libc", 243 | ] 244 | 245 | [[package]] 246 | name = "core-foundation-sys" 247 | version = "0.7.0" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" 250 | 251 | [[package]] 252 | name = "core-foundation-sys" 253 | version = "0.8.2" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" 256 | 257 | [[package]] 258 | name = "core-graphics" 259 | version = "0.19.2" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" 262 | dependencies = [ 263 | "bitflags", 264 | "core-foundation 0.7.0", 265 | "foreign-types", 266 | "libc", 267 | ] 268 | 269 | [[package]] 270 | name = "core-graphics" 271 | version = "0.22.2" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86" 274 | dependencies = [ 275 | "bitflags", 276 | "core-foundation 0.9.1", 277 | "core-graphics-types", 278 | "foreign-types", 279 | "libc", 280 | ] 281 | 282 | [[package]] 283 | name = "core-graphics-types" 284 | version = "0.1.1" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" 287 | dependencies = [ 288 | "bitflags", 289 | "core-foundation 0.9.1", 290 | "foreign-types", 291 | "libc", 292 | ] 293 | 294 | [[package]] 295 | name = "core-text" 296 | version = "15.0.0" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "131b3fd1f8bd5db9f2b398fa4fdb6008c64afc04d447c306ac2c7e98fba2a61d" 299 | dependencies = [ 300 | "core-foundation 0.7.0", 301 | "core-graphics 0.19.2", 302 | "foreign-types", 303 | "libc", 304 | ] 305 | 306 | [[package]] 307 | name = "core-text" 308 | version = "19.1.0" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "d2c7f46e8b820fd5f4b28528104b28b0a91cbe9e9c5bde8017087fb44bc93a60" 311 | dependencies = [ 312 | "core-foundation 0.9.1", 313 | "core-graphics 0.22.2", 314 | "foreign-types", 315 | "libc", 316 | ] 317 | 318 | [[package]] 319 | name = "crc32fast" 320 | version = "1.2.1" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" 323 | dependencies = [ 324 | "cfg-if 1.0.0", 325 | ] 326 | 327 | [[package]] 328 | name = "data-url" 329 | version = "0.1.0" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "d33fe99ccedd6e84bc035f1931bb2e6be79739d6242bd895e7311c886c50dc9c" 332 | dependencies = [ 333 | "matches", 334 | ] 335 | 336 | [[package]] 337 | name = "discard" 338 | version = "1.0.4" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" 341 | 342 | [[package]] 343 | name = "druid" 344 | version = "0.6.0" 345 | source = "git+https://github.com/gmorenz/druid?branch=gregs-changes#d1b9331a765a022d55eea397d2b5200b5b9fafbf" 346 | dependencies = [ 347 | "console_log", 348 | "druid-derive", 349 | "druid-shell", 350 | "fluent-bundle", 351 | "fluent-langneg", 352 | "fluent-syntax", 353 | "fnv", 354 | "harfbuzz-sys", 355 | "im", 356 | "instant", 357 | "log", 358 | "simple_logger", 359 | "unic-langid", 360 | "unicode-segmentation", 361 | "usvg", 362 | "xi-unicode", 363 | ] 364 | 365 | [[package]] 366 | name = "druid-derive" 367 | version = "0.3.1" 368 | source = "git+https://github.com/gmorenz/druid?branch=gregs-changes#d1b9331a765a022d55eea397d2b5200b5b9fafbf" 369 | dependencies = [ 370 | "proc-macro2", 371 | "quote", 372 | "syn", 373 | ] 374 | 375 | [[package]] 376 | name = "druid-shell" 377 | version = "0.6.0" 378 | source = "git+https://github.com/gmorenz/druid?branch=gregs-changes#d1b9331a765a022d55eea397d2b5200b5b9fafbf" 379 | dependencies = [ 380 | "anyhow", 381 | "bitflags", 382 | "block", 383 | "cairo-rs", 384 | "cfg-if 0.1.10", 385 | "cocoa", 386 | "core-graphics 0.22.2", 387 | "foreign-types", 388 | "gdk", 389 | "gdk-pixbuf", 390 | "gdk-sys", 391 | "gio", 392 | "glib", 393 | "glib-sys", 394 | "gtk", 395 | "gtk-sys", 396 | "instant", 397 | "js-sys", 398 | "keyboard-types", 399 | "kurbo 0.7.1", 400 | "lazy_static", 401 | "log", 402 | "objc", 403 | "piet-common", 404 | "scopeguard", 405 | "time", 406 | "wasm-bindgen", 407 | "web-sys", 408 | "winapi", 409 | "wio", 410 | ] 411 | 412 | [[package]] 413 | name = "dwrote" 414 | version = "0.11.0" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" 417 | dependencies = [ 418 | "lazy_static", 419 | "libc", 420 | "winapi", 421 | "wio", 422 | ] 423 | 424 | [[package]] 425 | name = "either" 426 | version = "1.6.1" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 429 | 430 | [[package]] 431 | name = "flate2" 432 | version = "1.0.19" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129" 435 | dependencies = [ 436 | "cfg-if 1.0.0", 437 | "crc32fast", 438 | "libc", 439 | "miniz_oxide", 440 | ] 441 | 442 | [[package]] 443 | name = "float-cmp" 444 | version = "0.5.3" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "75224bec9bfe1a65e2d34132933f2de7fe79900c96a0174307554244ece8150e" 447 | 448 | [[package]] 449 | name = "fluent-bundle" 450 | version = "0.12.0" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "01a094d494ab2ed06077e9a95f4e47f446c376de95f6c93045dd88c499bfcd70" 453 | dependencies = [ 454 | "fluent-langneg", 455 | "fluent-syntax", 456 | "intl-memoizer", 457 | "intl_pluralrules", 458 | "rental", 459 | "smallvec 1.6.0", 460 | "unic-langid", 461 | ] 462 | 463 | [[package]] 464 | name = "fluent-langneg" 465 | version = "0.13.0" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94" 468 | dependencies = [ 469 | "unic-langid", 470 | ] 471 | 472 | [[package]] 473 | name = "fluent-syntax" 474 | version = "0.9.3" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "ac0f7e83d14cccbf26e165d8881dcac5891af0d85a88543c09dd72ebd31d91ba" 477 | 478 | [[package]] 479 | name = "fnv" 480 | version = "1.0.7" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 483 | 484 | [[package]] 485 | name = "foreign-types" 486 | version = "0.3.2" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 489 | dependencies = [ 490 | "foreign-types-shared", 491 | ] 492 | 493 | [[package]] 494 | name = "foreign-types-shared" 495 | version = "0.1.1" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 498 | 499 | [[package]] 500 | name = "freetype" 501 | version = "0.5.1" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "b73222ab32d9ad65fe0e1c3258da8d614fd47cf19fce92b09eb520060c5c5ad5" 504 | dependencies = [ 505 | "freetype-sys", 506 | "libc", 507 | ] 508 | 509 | [[package]] 510 | name = "freetype-sys" 511 | version = "0.11.0" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "d48ac0ce366dd47a115ec8e598d7c51b4a974fc52ded5e53a56b31f55f34f3ea" 514 | dependencies = [ 515 | "cmake", 516 | "libc", 517 | "pkg-config", 518 | ] 519 | 520 | [[package]] 521 | name = "futures" 522 | version = "0.3.8" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0" 525 | dependencies = [ 526 | "futures-channel", 527 | "futures-core", 528 | "futures-executor", 529 | "futures-io", 530 | "futures-sink", 531 | "futures-task", 532 | "futures-util", 533 | ] 534 | 535 | [[package]] 536 | name = "futures-channel" 537 | version = "0.3.8" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64" 540 | dependencies = [ 541 | "futures-core", 542 | "futures-sink", 543 | ] 544 | 545 | [[package]] 546 | name = "futures-core" 547 | version = "0.3.8" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748" 550 | 551 | [[package]] 552 | name = "futures-executor" 553 | version = "0.3.8" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "4caa2b2b68b880003057c1dd49f1ed937e38f22fcf6c212188a121f08cf40a65" 556 | dependencies = [ 557 | "futures-core", 558 | "futures-task", 559 | "futures-util", 560 | ] 561 | 562 | [[package]] 563 | name = "futures-io" 564 | version = "0.3.8" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb" 567 | 568 | [[package]] 569 | name = "futures-macro" 570 | version = "0.3.8" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556" 573 | dependencies = [ 574 | "proc-macro-hack", 575 | "proc-macro2", 576 | "quote", 577 | "syn", 578 | ] 579 | 580 | [[package]] 581 | name = "futures-sink" 582 | version = "0.3.8" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d" 585 | 586 | [[package]] 587 | name = "futures-task" 588 | version = "0.3.8" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d" 591 | dependencies = [ 592 | "once_cell", 593 | ] 594 | 595 | [[package]] 596 | name = "futures-util" 597 | version = "0.3.8" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2" 600 | dependencies = [ 601 | "futures-channel", 602 | "futures-core", 603 | "futures-io", 604 | "futures-macro", 605 | "futures-sink", 606 | "futures-task", 607 | "memchr", 608 | "pin-project", 609 | "pin-utils", 610 | "proc-macro-hack", 611 | "proc-macro-nested", 612 | "slab", 613 | ] 614 | 615 | [[package]] 616 | name = "fxhash" 617 | version = "0.2.1" 618 | source = "registry+https://github.com/rust-lang/crates.io-index" 619 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 620 | dependencies = [ 621 | "byteorder", 622 | ] 623 | 624 | [[package]] 625 | name = "gdk" 626 | version = "0.13.2" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "db00839b2a68a7a10af3fa28dfb3febaba3a20c3a9ac2425a33b7df1f84a6b7d" 629 | dependencies = [ 630 | "bitflags", 631 | "cairo-rs", 632 | "cairo-sys-rs", 633 | "gdk-pixbuf", 634 | "gdk-sys", 635 | "gio", 636 | "gio-sys", 637 | "glib", 638 | "glib-sys", 639 | "gobject-sys", 640 | "libc", 641 | "pango", 642 | ] 643 | 644 | [[package]] 645 | name = "gdk-pixbuf" 646 | version = "0.9.0" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | checksum = "8f6dae3cb99dd49b758b88f0132f8d401108e63ae8edd45f432d42cdff99998a" 649 | dependencies = [ 650 | "gdk-pixbuf-sys", 651 | "gio", 652 | "gio-sys", 653 | "glib", 654 | "glib-sys", 655 | "gobject-sys", 656 | "libc", 657 | ] 658 | 659 | [[package]] 660 | name = "gdk-pixbuf-sys" 661 | version = "0.10.0" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | checksum = "3bfe468a7f43e97b8d193a762b6c5cf67a7d36cacbc0b9291dbcae24bfea1e8f" 664 | dependencies = [ 665 | "gio-sys", 666 | "glib-sys", 667 | "gobject-sys", 668 | "libc", 669 | "system-deps", 670 | ] 671 | 672 | [[package]] 673 | name = "gdk-sys" 674 | version = "0.10.0" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "0a9653cfc500fd268015b1ac055ddbc3df7a5c9ea3f4ccef147b3957bd140d69" 677 | dependencies = [ 678 | "cairo-sys-rs", 679 | "gdk-pixbuf-sys", 680 | "gio-sys", 681 | "glib-sys", 682 | "gobject-sys", 683 | "libc", 684 | "pango-sys", 685 | "pkg-config", 686 | "system-deps", 687 | ] 688 | 689 | [[package]] 690 | name = "gio" 691 | version = "0.9.1" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "1fb60242bfff700772dae5d9e3a1f7aa2e4ebccf18b89662a16acb2822568561" 694 | dependencies = [ 695 | "bitflags", 696 | "futures", 697 | "futures-channel", 698 | "futures-core", 699 | "futures-io", 700 | "futures-util", 701 | "gio-sys", 702 | "glib", 703 | "glib-sys", 704 | "gobject-sys", 705 | "libc", 706 | "once_cell", 707 | "thiserror", 708 | ] 709 | 710 | [[package]] 711 | name = "gio-sys" 712 | version = "0.10.1" 713 | source = "registry+https://github.com/rust-lang/crates.io-index" 714 | checksum = "5e24fb752f8f5d2cf6bbc2c606fd2bc989c81c5e2fe321ab974d54f8b6344eac" 715 | dependencies = [ 716 | "glib-sys", 717 | "gobject-sys", 718 | "libc", 719 | "system-deps", 720 | "winapi", 721 | ] 722 | 723 | [[package]] 724 | name = "glib" 725 | version = "0.10.3" 726 | source = "registry+https://github.com/rust-lang/crates.io-index" 727 | checksum = "0c685013b7515e668f1b57a165b009d4d28cb139a8a989bbd699c10dad29d0c5" 728 | dependencies = [ 729 | "bitflags", 730 | "futures-channel", 731 | "futures-core", 732 | "futures-executor", 733 | "futures-task", 734 | "futures-util", 735 | "glib-macros", 736 | "glib-sys", 737 | "gobject-sys", 738 | "libc", 739 | "once_cell", 740 | ] 741 | 742 | [[package]] 743 | name = "glib-macros" 744 | version = "0.10.1" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | checksum = "41486a26d1366a8032b160b59065a59fb528530a46a49f627e7048fb8c064039" 747 | dependencies = [ 748 | "anyhow", 749 | "heck", 750 | "itertools", 751 | "proc-macro-crate", 752 | "proc-macro-error", 753 | "proc-macro2", 754 | "quote", 755 | "syn", 756 | ] 757 | 758 | [[package]] 759 | name = "glib-sys" 760 | version = "0.10.1" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "c7e9b997a66e9a23d073f2b1abb4dbfc3925e0b8952f67efd8d9b6e168e4cdc1" 763 | dependencies = [ 764 | "libc", 765 | "system-deps", 766 | ] 767 | 768 | [[package]] 769 | name = "gobject-sys" 770 | version = "0.10.0" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "952133b60c318a62bf82ee75b93acc7e84028a093e06b9e27981c2b6fe68218c" 773 | dependencies = [ 774 | "glib-sys", 775 | "libc", 776 | "system-deps", 777 | ] 778 | 779 | [[package]] 780 | name = "gtk" 781 | version = "0.9.2" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "2f022f2054072b3af07666341984562c8e626a79daa8be27b955d12d06a5ad6a" 784 | dependencies = [ 785 | "atk", 786 | "bitflags", 787 | "cairo-rs", 788 | "cairo-sys-rs", 789 | "cc", 790 | "gdk", 791 | "gdk-pixbuf", 792 | "gdk-pixbuf-sys", 793 | "gdk-sys", 794 | "gio", 795 | "gio-sys", 796 | "glib", 797 | "glib-sys", 798 | "gobject-sys", 799 | "gtk-sys", 800 | "libc", 801 | "once_cell", 802 | "pango", 803 | "pango-sys", 804 | "pkg-config", 805 | ] 806 | 807 | [[package]] 808 | name = "gtk-sys" 809 | version = "0.10.0" 810 | source = "registry+https://github.com/rust-lang/crates.io-index" 811 | checksum = "89acda6f084863307d948ba64a4b1ef674e8527dddab147ee4cdcc194c880457" 812 | dependencies = [ 813 | "atk-sys", 814 | "cairo-sys-rs", 815 | "gdk-pixbuf-sys", 816 | "gdk-sys", 817 | "gio-sys", 818 | "glib-sys", 819 | "gobject-sys", 820 | "libc", 821 | "pango-sys", 822 | "system-deps", 823 | ] 824 | 825 | [[package]] 826 | name = "harfbuzz-sys" 827 | version = "0.4.1" 828 | source = "registry+https://github.com/rust-lang/crates.io-index" 829 | checksum = "845f3e65ec4e30b0b1b6e1c055900871f3776d3492cc76744e3fc5943a6129a9" 830 | dependencies = [ 831 | "cc", 832 | "core-graphics 0.19.2", 833 | "core-text 15.0.0", 834 | "foreign-types", 835 | "freetype", 836 | "pkg-config", 837 | ] 838 | 839 | [[package]] 840 | name = "harfbuzz_rs" 841 | version = "1.2.0" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | checksum = "d5bba81dd6b356135f0b31c42447e49d45116adc2e02910070947a322c423aa5" 844 | dependencies = [ 845 | "bitflags", 846 | "harfbuzz-sys", 847 | ] 848 | 849 | [[package]] 850 | name = "heck" 851 | version = "0.3.2" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" 854 | dependencies = [ 855 | "unicode-segmentation", 856 | ] 857 | 858 | [[package]] 859 | name = "hermit-abi" 860 | version = "0.1.17" 861 | source = "registry+https://github.com/rust-lang/crates.io-index" 862 | checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" 863 | dependencies = [ 864 | "libc", 865 | ] 866 | 867 | [[package]] 868 | name = "im" 869 | version = "15.0.0" 870 | source = "registry+https://github.com/rust-lang/crates.io-index" 871 | checksum = "111c1983f3c5bb72732df25cddacee9b546d08325fb584b5ebd38148be7b0246" 872 | dependencies = [ 873 | "bitmaps", 874 | "rand_core", 875 | "rand_xoshiro", 876 | "sized-chunks", 877 | "typenum", 878 | "version_check", 879 | ] 880 | 881 | [[package]] 882 | name = "instant" 883 | version = "0.1.9" 884 | source = "registry+https://github.com/rust-lang/crates.io-index" 885 | checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" 886 | dependencies = [ 887 | "cfg-if 1.0.0", 888 | "js-sys", 889 | "wasm-bindgen", 890 | "web-sys", 891 | ] 892 | 893 | [[package]] 894 | name = "intl-memoizer" 895 | version = "0.5.0" 896 | source = "registry+https://github.com/rust-lang/crates.io-index" 897 | checksum = "8a0ed58ba6089d49f8a9a7d5e16fc9b9e2019cdf40ef270f3d465fa244d9630b" 898 | dependencies = [ 899 | "type-map", 900 | "unic-langid", 901 | ] 902 | 903 | [[package]] 904 | name = "intl_pluralrules" 905 | version = "7.0.0" 906 | source = "registry+https://github.com/rust-lang/crates.io-index" 907 | checksum = "6c271cdb1f12a9feb3a017619c3ee681f971f270f6757341d6abe1f9f7a98bc3" 908 | dependencies = [ 909 | "tinystr", 910 | "unic-langid", 911 | ] 912 | 913 | [[package]] 914 | name = "itertools" 915 | version = "0.9.0" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" 918 | dependencies = [ 919 | "either", 920 | ] 921 | 922 | [[package]] 923 | name = "itoa" 924 | version = "0.4.7" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 927 | 928 | [[package]] 929 | name = "js-sys" 930 | version = "0.3.46" 931 | source = "registry+https://github.com/rust-lang/crates.io-index" 932 | checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" 933 | dependencies = [ 934 | "wasm-bindgen", 935 | ] 936 | 937 | [[package]] 938 | name = "keyboard-types" 939 | version = "0.5.0" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | checksum = "a989afac88279b0482f402d234b5fbd405bf1ad051308595b58de4e6de22346b" 942 | dependencies = [ 943 | "bitflags", 944 | ] 945 | 946 | [[package]] 947 | name = "kurbo" 948 | version = "0.6.3" 949 | source = "registry+https://github.com/rust-lang/crates.io-index" 950 | checksum = "ed9be561444b7e4c4ac33b4fa36af1f996ccca1f14840ea1a09ba034289502ed" 951 | dependencies = [ 952 | "arrayvec", 953 | ] 954 | 955 | [[package]] 956 | name = "kurbo" 957 | version = "0.7.1" 958 | source = "registry+https://github.com/rust-lang/crates.io-index" 959 | checksum = "16cb54cd28cb3d2e964d9444ca185676a94fd9b7cce5f02b22c717947ed8e9a2" 960 | dependencies = [ 961 | "arrayvec", 962 | ] 963 | 964 | [[package]] 965 | name = "lazy_static" 966 | version = "1.4.0" 967 | source = "registry+https://github.com/rust-lang/crates.io-index" 968 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 969 | 970 | [[package]] 971 | name = "libc" 972 | version = "0.2.81" 973 | source = "registry+https://github.com/rust-lang/crates.io-index" 974 | checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" 975 | 976 | [[package]] 977 | name = "log" 978 | version = "0.4.11" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 981 | dependencies = [ 982 | "cfg-if 0.1.10", 983 | ] 984 | 985 | [[package]] 986 | name = "malloc_buf" 987 | version = "0.0.6" 988 | source = "registry+https://github.com/rust-lang/crates.io-index" 989 | checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" 990 | dependencies = [ 991 | "libc", 992 | ] 993 | 994 | [[package]] 995 | name = "matches" 996 | version = "0.1.8" 997 | source = "registry+https://github.com/rust-lang/crates.io-index" 998 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 999 | 1000 | [[package]] 1001 | name = "maybe-uninit" 1002 | version = "2.0.0" 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" 1004 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" 1005 | 1006 | [[package]] 1007 | name = "memchr" 1008 | version = "2.3.4" 1009 | source = "registry+https://github.com/rust-lang/crates.io-index" 1010 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 1011 | 1012 | [[package]] 1013 | name = "memmap2" 1014 | version = "0.1.0" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a" 1017 | dependencies = [ 1018 | "libc", 1019 | ] 1020 | 1021 | [[package]] 1022 | name = "miniz_oxide" 1023 | version = "0.4.3" 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" 1025 | checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" 1026 | dependencies = [ 1027 | "adler", 1028 | "autocfg", 1029 | ] 1030 | 1031 | [[package]] 1032 | name = "objc" 1033 | version = "0.2.7" 1034 | source = "registry+https://github.com/rust-lang/crates.io-index" 1035 | checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" 1036 | dependencies = [ 1037 | "malloc_buf", 1038 | ] 1039 | 1040 | [[package]] 1041 | name = "once_cell" 1042 | version = "1.5.2" 1043 | source = "registry+https://github.com/rust-lang/crates.io-index" 1044 | checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" 1045 | 1046 | [[package]] 1047 | name = "pango" 1048 | version = "0.9.1" 1049 | source = "registry+https://github.com/rust-lang/crates.io-index" 1050 | checksum = "9937068580bebd8ced19975938573803273ccbcbd598c58d4906efd4ac87c438" 1051 | dependencies = [ 1052 | "bitflags", 1053 | "glib", 1054 | "glib-sys", 1055 | "gobject-sys", 1056 | "libc", 1057 | "once_cell", 1058 | "pango-sys", 1059 | ] 1060 | 1061 | [[package]] 1062 | name = "pango-sys" 1063 | version = "0.10.0" 1064 | source = "registry+https://github.com/rust-lang/crates.io-index" 1065 | checksum = "24d2650c8b62d116c020abd0cea26a4ed96526afda89b1c4ea567131fdefc890" 1066 | dependencies = [ 1067 | "glib-sys", 1068 | "gobject-sys", 1069 | "libc", 1070 | "system-deps", 1071 | ] 1072 | 1073 | [[package]] 1074 | name = "paste" 1075 | version = "0.1.18" 1076 | source = "registry+https://github.com/rust-lang/crates.io-index" 1077 | checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" 1078 | dependencies = [ 1079 | "paste-impl", 1080 | "proc-macro-hack", 1081 | ] 1082 | 1083 | [[package]] 1084 | name = "paste-impl" 1085 | version = "0.1.18" 1086 | source = "registry+https://github.com/rust-lang/crates.io-index" 1087 | checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" 1088 | dependencies = [ 1089 | "proc-macro-hack", 1090 | ] 1091 | 1092 | [[package]] 1093 | name = "pico-args" 1094 | version = "0.3.4" 1095 | source = "registry+https://github.com/rust-lang/crates.io-index" 1096 | checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1" 1097 | 1098 | [[package]] 1099 | name = "piet" 1100 | version = "0.2.0-pre6" 1101 | source = "registry+https://github.com/rust-lang/crates.io-index" 1102 | checksum = "b6516cf192cccc3037c3ba5405018f83e026e3b340110ef7f6ce58cbddd25cf2" 1103 | dependencies = [ 1104 | "kurbo 0.7.1", 1105 | ] 1106 | 1107 | [[package]] 1108 | name = "piet-cairo" 1109 | version = "0.2.0-pre6" 1110 | source = "registry+https://github.com/rust-lang/crates.io-index" 1111 | checksum = "c4dc74d4de04c0bafc4d2aa358c06356bc4a361d46d8f718150557902801a1d9" 1112 | dependencies = [ 1113 | "cairo-rs", 1114 | "piet", 1115 | "unicode-segmentation", 1116 | "xi-unicode", 1117 | ] 1118 | 1119 | [[package]] 1120 | name = "piet-common" 1121 | version = "0.2.0-pre6" 1122 | source = "registry+https://github.com/rust-lang/crates.io-index" 1123 | checksum = "c3e165053bbd04cac97cf7c3de85e64326b6d7a99ee5782d2022a8fd5d502953" 1124 | dependencies = [ 1125 | "cairo-rs", 1126 | "cairo-sys-rs", 1127 | "cfg-if 1.0.0", 1128 | "core-graphics 0.22.2", 1129 | "piet", 1130 | "piet-cairo", 1131 | "piet-coregraphics", 1132 | "piet-direct2d", 1133 | "piet-web", 1134 | "wasm-bindgen", 1135 | "web-sys", 1136 | ] 1137 | 1138 | [[package]] 1139 | name = "piet-coregraphics" 1140 | version = "0.2.0-pre6" 1141 | source = "registry+https://github.com/rust-lang/crates.io-index" 1142 | checksum = "f9c8d68ea9ff581e5e3f468b562982ee78a1f3568353e6860e26c5a6a1080f15" 1143 | dependencies = [ 1144 | "core-foundation 0.9.1", 1145 | "core-foundation-sys 0.8.2", 1146 | "core-graphics 0.22.2", 1147 | "core-text 19.1.0", 1148 | "foreign-types", 1149 | "piet", 1150 | "unic-bidi", 1151 | ] 1152 | 1153 | [[package]] 1154 | name = "piet-direct2d" 1155 | version = "0.2.0-pre6" 1156 | source = "registry+https://github.com/rust-lang/crates.io-index" 1157 | checksum = "0b74ea11318f93d52bf1c8acbb6a7ad8e09728e9321fff66847b0ae7bd097f15" 1158 | dependencies = [ 1159 | "associative-cache", 1160 | "dwrote", 1161 | "piet", 1162 | "utf16_lit", 1163 | "winapi", 1164 | "wio", 1165 | ] 1166 | 1167 | [[package]] 1168 | name = "piet-web" 1169 | version = "0.2.0-pre6" 1170 | source = "registry+https://github.com/rust-lang/crates.io-index" 1171 | checksum = "47db6c4d7c71d4e8d1f50caf838ef99d9fad4a0d831d3026ba7716425c2e62ac" 1172 | dependencies = [ 1173 | "js-sys", 1174 | "piet", 1175 | "unicode-segmentation", 1176 | "wasm-bindgen", 1177 | "web-sys", 1178 | "xi-unicode", 1179 | ] 1180 | 1181 | [[package]] 1182 | name = "pin-project" 1183 | version = "1.0.2" 1184 | source = "registry+https://github.com/rust-lang/crates.io-index" 1185 | checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7" 1186 | dependencies = [ 1187 | "pin-project-internal", 1188 | ] 1189 | 1190 | [[package]] 1191 | name = "pin-project-internal" 1192 | version = "1.0.2" 1193 | source = "registry+https://github.com/rust-lang/crates.io-index" 1194 | checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f" 1195 | dependencies = [ 1196 | "proc-macro2", 1197 | "quote", 1198 | "syn", 1199 | ] 1200 | 1201 | [[package]] 1202 | name = "pin-utils" 1203 | version = "0.1.0" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1206 | 1207 | [[package]] 1208 | name = "pkg-config" 1209 | version = "0.3.19" 1210 | source = "registry+https://github.com/rust-lang/crates.io-index" 1211 | checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" 1212 | 1213 | [[package]] 1214 | name = "proc-macro-crate" 1215 | version = "0.1.5" 1216 | source = "registry+https://github.com/rust-lang/crates.io-index" 1217 | checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" 1218 | dependencies = [ 1219 | "toml", 1220 | ] 1221 | 1222 | [[package]] 1223 | name = "proc-macro-error" 1224 | version = "1.0.4" 1225 | source = "registry+https://github.com/rust-lang/crates.io-index" 1226 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 1227 | dependencies = [ 1228 | "proc-macro-error-attr", 1229 | "proc-macro2", 1230 | "quote", 1231 | "syn", 1232 | "version_check", 1233 | ] 1234 | 1235 | [[package]] 1236 | name = "proc-macro-error-attr" 1237 | version = "1.0.4" 1238 | source = "registry+https://github.com/rust-lang/crates.io-index" 1239 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 1240 | dependencies = [ 1241 | "proc-macro2", 1242 | "quote", 1243 | "version_check", 1244 | ] 1245 | 1246 | [[package]] 1247 | name = "proc-macro-hack" 1248 | version = "0.5.19" 1249 | source = "registry+https://github.com/rust-lang/crates.io-index" 1250 | checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" 1251 | 1252 | [[package]] 1253 | name = "proc-macro-nested" 1254 | version = "0.1.6" 1255 | source = "registry+https://github.com/rust-lang/crates.io-index" 1256 | checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" 1257 | 1258 | [[package]] 1259 | name = "proc-macro2" 1260 | version = "1.0.24" 1261 | source = "registry+https://github.com/rust-lang/crates.io-index" 1262 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 1263 | dependencies = [ 1264 | "unicode-xid", 1265 | ] 1266 | 1267 | [[package]] 1268 | name = "qc" 1269 | version = "0.1.0" 1270 | dependencies = [ 1271 | "aljabar", 1272 | "druid", 1273 | "im", 1274 | ] 1275 | 1276 | [[package]] 1277 | name = "quote" 1278 | version = "1.0.8" 1279 | source = "registry+https://github.com/rust-lang/crates.io-index" 1280 | checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" 1281 | dependencies = [ 1282 | "proc-macro2", 1283 | ] 1284 | 1285 | [[package]] 1286 | name = "rand_core" 1287 | version = "0.5.1" 1288 | source = "registry+https://github.com/rust-lang/crates.io-index" 1289 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 1290 | 1291 | [[package]] 1292 | name = "rand_xoshiro" 1293 | version = "0.4.0" 1294 | source = "registry+https://github.com/rust-lang/crates.io-index" 1295 | checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004" 1296 | dependencies = [ 1297 | "rand_core", 1298 | ] 1299 | 1300 | [[package]] 1301 | name = "rctree" 1302 | version = "0.3.3" 1303 | source = "registry+https://github.com/rust-lang/crates.io-index" 1304 | checksum = "be9e29cb19c8fe84169fcb07f8f11e66bc9e6e0280efd4715c54818296f8a4a8" 1305 | 1306 | [[package]] 1307 | name = "rental" 1308 | version = "0.5.5" 1309 | source = "registry+https://github.com/rust-lang/crates.io-index" 1310 | checksum = "8545debe98b2b139fb04cad8618b530e9b07c152d99a5de83c860b877d67847f" 1311 | dependencies = [ 1312 | "rental-impl", 1313 | "stable_deref_trait", 1314 | ] 1315 | 1316 | [[package]] 1317 | name = "rental-impl" 1318 | version = "0.5.5" 1319 | source = "registry+https://github.com/rust-lang/crates.io-index" 1320 | checksum = "475e68978dc5b743f2f40d8e0a8fdc83f1c5e78cbf4b8fa5e74e73beebc340de" 1321 | dependencies = [ 1322 | "proc-macro2", 1323 | "quote", 1324 | "syn", 1325 | ] 1326 | 1327 | [[package]] 1328 | name = "roxmltree" 1329 | version = "0.11.0" 1330 | source = "registry+https://github.com/rust-lang/crates.io-index" 1331 | checksum = "d5001f134077069d87f77c8b9452b690df2445f7a43f1c7ca4a1af8dd505789d" 1332 | dependencies = [ 1333 | "xmlparser", 1334 | ] 1335 | 1336 | [[package]] 1337 | name = "rustc_version" 1338 | version = "0.2.3" 1339 | source = "registry+https://github.com/rust-lang/crates.io-index" 1340 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 1341 | dependencies = [ 1342 | "semver", 1343 | ] 1344 | 1345 | [[package]] 1346 | name = "ryu" 1347 | version = "1.0.5" 1348 | source = "registry+https://github.com/rust-lang/crates.io-index" 1349 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 1350 | 1351 | [[package]] 1352 | name = "scopeguard" 1353 | version = "1.1.0" 1354 | source = "registry+https://github.com/rust-lang/crates.io-index" 1355 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1356 | 1357 | [[package]] 1358 | name = "semver" 1359 | version = "0.9.0" 1360 | source = "registry+https://github.com/rust-lang/crates.io-index" 1361 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 1362 | dependencies = [ 1363 | "semver-parser", 1364 | ] 1365 | 1366 | [[package]] 1367 | name = "semver-parser" 1368 | version = "0.7.0" 1369 | source = "registry+https://github.com/rust-lang/crates.io-index" 1370 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 1371 | 1372 | [[package]] 1373 | name = "serde" 1374 | version = "1.0.118" 1375 | source = "registry+https://github.com/rust-lang/crates.io-index" 1376 | checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" 1377 | 1378 | [[package]] 1379 | name = "serde_derive" 1380 | version = "1.0.118" 1381 | source = "registry+https://github.com/rust-lang/crates.io-index" 1382 | checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" 1383 | dependencies = [ 1384 | "proc-macro2", 1385 | "quote", 1386 | "syn", 1387 | ] 1388 | 1389 | [[package]] 1390 | name = "serde_json" 1391 | version = "1.0.61" 1392 | source = "registry+https://github.com/rust-lang/crates.io-index" 1393 | checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" 1394 | dependencies = [ 1395 | "itoa", 1396 | "ryu", 1397 | "serde", 1398 | ] 1399 | 1400 | [[package]] 1401 | name = "sha1" 1402 | version = "0.6.0" 1403 | source = "registry+https://github.com/rust-lang/crates.io-index" 1404 | checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" 1405 | 1406 | [[package]] 1407 | name = "simple_logger" 1408 | version = "1.11.0" 1409 | source = "registry+https://github.com/rust-lang/crates.io-index" 1410 | checksum = "cd57f17c093ead1d4a1499dc9acaafdd71240908d64775465543b8d9a9f1d198" 1411 | dependencies = [ 1412 | "atty", 1413 | "log", 1414 | "winapi", 1415 | ] 1416 | 1417 | [[package]] 1418 | name = "simplecss" 1419 | version = "0.2.0" 1420 | source = "registry+https://github.com/rust-lang/crates.io-index" 1421 | checksum = "596554e63596d556a0dbd681416342ca61c75f1a45203201e7e77d3fa2fa9014" 1422 | dependencies = [ 1423 | "log", 1424 | ] 1425 | 1426 | [[package]] 1427 | name = "siphasher" 1428 | version = "0.2.3" 1429 | source = "registry+https://github.com/rust-lang/crates.io-index" 1430 | checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" 1431 | 1432 | [[package]] 1433 | name = "sized-chunks" 1434 | version = "0.6.2" 1435 | source = "registry+https://github.com/rust-lang/crates.io-index" 1436 | checksum = "1ec31ceca5644fa6d444cc77548b88b67f46db6f7c71683b0f9336e671830d2f" 1437 | dependencies = [ 1438 | "bitmaps", 1439 | "typenum", 1440 | ] 1441 | 1442 | [[package]] 1443 | name = "slab" 1444 | version = "0.4.2" 1445 | source = "registry+https://github.com/rust-lang/crates.io-index" 1446 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 1447 | 1448 | [[package]] 1449 | name = "smallvec" 1450 | version = "0.6.13" 1451 | source = "registry+https://github.com/rust-lang/crates.io-index" 1452 | checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" 1453 | dependencies = [ 1454 | "maybe-uninit", 1455 | ] 1456 | 1457 | [[package]] 1458 | name = "smallvec" 1459 | version = "1.6.0" 1460 | source = "registry+https://github.com/rust-lang/crates.io-index" 1461 | checksum = "1a55ca5f3b68e41c979bf8c46a6f1da892ca4db8f94023ce0bd32407573b1ac0" 1462 | 1463 | [[package]] 1464 | name = "stable_deref_trait" 1465 | version = "1.2.0" 1466 | source = "registry+https://github.com/rust-lang/crates.io-index" 1467 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1468 | 1469 | [[package]] 1470 | name = "standback" 1471 | version = "0.2.14" 1472 | source = "registry+https://github.com/rust-lang/crates.io-index" 1473 | checksum = "c66a8cff4fa24853fdf6b51f75c6d7f8206d7c75cab4e467bcd7f25c2b1febe0" 1474 | dependencies = [ 1475 | "version_check", 1476 | ] 1477 | 1478 | [[package]] 1479 | name = "stdweb" 1480 | version = "0.4.20" 1481 | source = "registry+https://github.com/rust-lang/crates.io-index" 1482 | checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" 1483 | dependencies = [ 1484 | "discard", 1485 | "rustc_version", 1486 | "stdweb-derive", 1487 | "stdweb-internal-macros", 1488 | "stdweb-internal-runtime", 1489 | "wasm-bindgen", 1490 | ] 1491 | 1492 | [[package]] 1493 | name = "stdweb-derive" 1494 | version = "0.5.3" 1495 | source = "registry+https://github.com/rust-lang/crates.io-index" 1496 | checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" 1497 | dependencies = [ 1498 | "proc-macro2", 1499 | "quote", 1500 | "serde", 1501 | "serde_derive", 1502 | "syn", 1503 | ] 1504 | 1505 | [[package]] 1506 | name = "stdweb-internal-macros" 1507 | version = "0.2.9" 1508 | source = "registry+https://github.com/rust-lang/crates.io-index" 1509 | checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" 1510 | dependencies = [ 1511 | "base-x", 1512 | "proc-macro2", 1513 | "quote", 1514 | "serde", 1515 | "serde_derive", 1516 | "serde_json", 1517 | "sha1", 1518 | "syn", 1519 | ] 1520 | 1521 | [[package]] 1522 | name = "stdweb-internal-runtime" 1523 | version = "0.1.5" 1524 | source = "registry+https://github.com/rust-lang/crates.io-index" 1525 | checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" 1526 | 1527 | [[package]] 1528 | name = "strum" 1529 | version = "0.18.0" 1530 | source = "registry+https://github.com/rust-lang/crates.io-index" 1531 | checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" 1532 | 1533 | [[package]] 1534 | name = "strum_macros" 1535 | version = "0.18.0" 1536 | source = "registry+https://github.com/rust-lang/crates.io-index" 1537 | checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" 1538 | dependencies = [ 1539 | "heck", 1540 | "proc-macro2", 1541 | "quote", 1542 | "syn", 1543 | ] 1544 | 1545 | [[package]] 1546 | name = "svgtypes" 1547 | version = "0.5.0" 1548 | source = "registry+https://github.com/rust-lang/crates.io-index" 1549 | checksum = "9c536faaff1a10837cfe373142583f6e27d81e96beba339147e77b67c9f260ff" 1550 | dependencies = [ 1551 | "float-cmp", 1552 | "siphasher", 1553 | ] 1554 | 1555 | [[package]] 1556 | name = "syn" 1557 | version = "1.0.57" 1558 | source = "registry+https://github.com/rust-lang/crates.io-index" 1559 | checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6" 1560 | dependencies = [ 1561 | "proc-macro2", 1562 | "quote", 1563 | "unicode-xid", 1564 | ] 1565 | 1566 | [[package]] 1567 | name = "system-deps" 1568 | version = "1.3.2" 1569 | source = "registry+https://github.com/rust-lang/crates.io-index" 1570 | checksum = "0f3ecc17269a19353b3558b313bba738b25d82993e30d62a18406a24aba4649b" 1571 | dependencies = [ 1572 | "heck", 1573 | "pkg-config", 1574 | "strum", 1575 | "strum_macros", 1576 | "thiserror", 1577 | "toml", 1578 | "version-compare", 1579 | ] 1580 | 1581 | [[package]] 1582 | name = "thiserror" 1583 | version = "1.0.23" 1584 | source = "registry+https://github.com/rust-lang/crates.io-index" 1585 | checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" 1586 | dependencies = [ 1587 | "thiserror-impl", 1588 | ] 1589 | 1590 | [[package]] 1591 | name = "thiserror-impl" 1592 | version = "1.0.23" 1593 | source = "registry+https://github.com/rust-lang/crates.io-index" 1594 | checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" 1595 | dependencies = [ 1596 | "proc-macro2", 1597 | "quote", 1598 | "syn", 1599 | ] 1600 | 1601 | [[package]] 1602 | name = "time" 1603 | version = "0.2.23" 1604 | source = "registry+https://github.com/rust-lang/crates.io-index" 1605 | checksum = "bcdaeea317915d59b2b4cd3b5efcd156c309108664277793f5351700c02ce98b" 1606 | dependencies = [ 1607 | "const_fn", 1608 | "libc", 1609 | "standback", 1610 | "stdweb", 1611 | "time-macros", 1612 | "version_check", 1613 | "winapi", 1614 | ] 1615 | 1616 | [[package]] 1617 | name = "time-macros" 1618 | version = "0.1.1" 1619 | source = "registry+https://github.com/rust-lang/crates.io-index" 1620 | checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" 1621 | dependencies = [ 1622 | "proc-macro-hack", 1623 | "time-macros-impl", 1624 | ] 1625 | 1626 | [[package]] 1627 | name = "time-macros-impl" 1628 | version = "0.1.1" 1629 | source = "registry+https://github.com/rust-lang/crates.io-index" 1630 | checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" 1631 | dependencies = [ 1632 | "proc-macro-hack", 1633 | "proc-macro2", 1634 | "quote", 1635 | "standback", 1636 | "syn", 1637 | ] 1638 | 1639 | [[package]] 1640 | name = "tinystr" 1641 | version = "0.3.4" 1642 | source = "registry+https://github.com/rust-lang/crates.io-index" 1643 | checksum = "29738eedb4388d9ea620eeab9384884fc3f06f586a2eddb56bedc5885126c7c1" 1644 | 1645 | [[package]] 1646 | name = "toml" 1647 | version = "0.5.8" 1648 | source = "registry+https://github.com/rust-lang/crates.io-index" 1649 | checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" 1650 | dependencies = [ 1651 | "serde", 1652 | ] 1653 | 1654 | [[package]] 1655 | name = "ttf-parser" 1656 | version = "0.6.2" 1657 | source = "registry+https://github.com/rust-lang/crates.io-index" 1658 | checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" 1659 | 1660 | [[package]] 1661 | name = "type-map" 1662 | version = "0.3.0" 1663 | source = "registry+https://github.com/rust-lang/crates.io-index" 1664 | checksum = "9d2741b1474c327d95c1f1e3b0a2c3977c8e128409c572a33af2914e7d636717" 1665 | dependencies = [ 1666 | "fxhash", 1667 | ] 1668 | 1669 | [[package]] 1670 | name = "typenum" 1671 | version = "1.12.0" 1672 | source = "registry+https://github.com/rust-lang/crates.io-index" 1673 | checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" 1674 | 1675 | [[package]] 1676 | name = "unic-bidi" 1677 | version = "0.9.0" 1678 | source = "registry+https://github.com/rust-lang/crates.io-index" 1679 | checksum = "1356b759fb6a82050666f11dce4b6fe3571781f1449f3ef78074e408d468ec09" 1680 | dependencies = [ 1681 | "matches", 1682 | "unic-ucd-bidi", 1683 | ] 1684 | 1685 | [[package]] 1686 | name = "unic-char-property" 1687 | version = "0.9.0" 1688 | source = "registry+https://github.com/rust-lang/crates.io-index" 1689 | checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" 1690 | dependencies = [ 1691 | "unic-char-range", 1692 | ] 1693 | 1694 | [[package]] 1695 | name = "unic-char-range" 1696 | version = "0.9.0" 1697 | source = "registry+https://github.com/rust-lang/crates.io-index" 1698 | checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" 1699 | 1700 | [[package]] 1701 | name = "unic-common" 1702 | version = "0.9.0" 1703 | source = "registry+https://github.com/rust-lang/crates.io-index" 1704 | checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" 1705 | 1706 | [[package]] 1707 | name = "unic-langid" 1708 | version = "0.9.0" 1709 | source = "registry+https://github.com/rust-lang/crates.io-index" 1710 | checksum = "73328fcd730a030bdb19ddf23e192187a6b01cd98be6d3140622a89129459ce5" 1711 | dependencies = [ 1712 | "unic-langid-impl", 1713 | ] 1714 | 1715 | [[package]] 1716 | name = "unic-langid-impl" 1717 | version = "0.9.0" 1718 | source = "registry+https://github.com/rust-lang/crates.io-index" 1719 | checksum = "1a4a8eeaf0494862c1404c95ec2f4c33a2acff5076f64314b465e3ddae1b934d" 1720 | dependencies = [ 1721 | "tinystr", 1722 | ] 1723 | 1724 | [[package]] 1725 | name = "unic-ucd-bidi" 1726 | version = "0.9.0" 1727 | source = "registry+https://github.com/rust-lang/crates.io-index" 1728 | checksum = "d1d568b51222484e1f8209ce48caa6b430bf352962b877d592c29ab31fb53d8c" 1729 | dependencies = [ 1730 | "unic-char-property", 1731 | "unic-char-range", 1732 | "unic-ucd-version", 1733 | ] 1734 | 1735 | [[package]] 1736 | name = "unic-ucd-version" 1737 | version = "0.9.0" 1738 | source = "registry+https://github.com/rust-lang/crates.io-index" 1739 | checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" 1740 | dependencies = [ 1741 | "unic-common", 1742 | ] 1743 | 1744 | [[package]] 1745 | name = "unicode-bidi" 1746 | version = "0.3.4" 1747 | source = "registry+https://github.com/rust-lang/crates.io-index" 1748 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 1749 | dependencies = [ 1750 | "matches", 1751 | ] 1752 | 1753 | [[package]] 1754 | name = "unicode-script" 1755 | version = "0.5.2" 1756 | source = "registry+https://github.com/rust-lang/crates.io-index" 1757 | checksum = "79bf4d5fc96546fdb73f9827097810bbda93b11a6770ff3a54e1f445d4135787" 1758 | 1759 | [[package]] 1760 | name = "unicode-segmentation" 1761 | version = "1.7.1" 1762 | source = "registry+https://github.com/rust-lang/crates.io-index" 1763 | checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" 1764 | 1765 | [[package]] 1766 | name = "unicode-vo" 1767 | version = "0.1.0" 1768 | source = "registry+https://github.com/rust-lang/crates.io-index" 1769 | checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" 1770 | 1771 | [[package]] 1772 | name = "unicode-xid" 1773 | version = "0.2.1" 1774 | source = "registry+https://github.com/rust-lang/crates.io-index" 1775 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 1776 | 1777 | [[package]] 1778 | name = "usvg" 1779 | version = "0.10.0" 1780 | source = "registry+https://github.com/rust-lang/crates.io-index" 1781 | checksum = "d681fd78e7926787a4e23c67beb18472525a10cb161d98e12f8a0a751f699ef8" 1782 | dependencies = [ 1783 | "base64", 1784 | "data-url", 1785 | "flate2", 1786 | "harfbuzz_rs", 1787 | "kurbo 0.6.3", 1788 | "log", 1789 | "memmap2", 1790 | "pico-args", 1791 | "rctree", 1792 | "roxmltree", 1793 | "simplecss", 1794 | "siphasher", 1795 | "svgtypes", 1796 | "ttf-parser", 1797 | "unicode-bidi", 1798 | "unicode-script", 1799 | "unicode-vo", 1800 | "xmlwriter", 1801 | ] 1802 | 1803 | [[package]] 1804 | name = "utf16_lit" 1805 | version = "1.0.1" 1806 | source = "registry+https://github.com/rust-lang/crates.io-index" 1807 | checksum = "403231d2fb8f0f55daf24cd03571f4fc1bb856e0fa98c1832bbae3f6ff7e7a07" 1808 | 1809 | [[package]] 1810 | name = "version-compare" 1811 | version = "0.0.10" 1812 | source = "registry+https://github.com/rust-lang/crates.io-index" 1813 | checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" 1814 | 1815 | [[package]] 1816 | name = "version_check" 1817 | version = "0.9.2" 1818 | source = "registry+https://github.com/rust-lang/crates.io-index" 1819 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 1820 | 1821 | [[package]] 1822 | name = "wasm-bindgen" 1823 | version = "0.2.69" 1824 | source = "registry+https://github.com/rust-lang/crates.io-index" 1825 | checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" 1826 | dependencies = [ 1827 | "cfg-if 1.0.0", 1828 | "wasm-bindgen-macro", 1829 | ] 1830 | 1831 | [[package]] 1832 | name = "wasm-bindgen-backend" 1833 | version = "0.2.69" 1834 | source = "registry+https://github.com/rust-lang/crates.io-index" 1835 | checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" 1836 | dependencies = [ 1837 | "bumpalo", 1838 | "lazy_static", 1839 | "log", 1840 | "proc-macro2", 1841 | "quote", 1842 | "syn", 1843 | "wasm-bindgen-shared", 1844 | ] 1845 | 1846 | [[package]] 1847 | name = "wasm-bindgen-macro" 1848 | version = "0.2.69" 1849 | source = "registry+https://github.com/rust-lang/crates.io-index" 1850 | checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" 1851 | dependencies = [ 1852 | "quote", 1853 | "wasm-bindgen-macro-support", 1854 | ] 1855 | 1856 | [[package]] 1857 | name = "wasm-bindgen-macro-support" 1858 | version = "0.2.69" 1859 | source = "registry+https://github.com/rust-lang/crates.io-index" 1860 | checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" 1861 | dependencies = [ 1862 | "proc-macro2", 1863 | "quote", 1864 | "syn", 1865 | "wasm-bindgen-backend", 1866 | "wasm-bindgen-shared", 1867 | ] 1868 | 1869 | [[package]] 1870 | name = "wasm-bindgen-shared" 1871 | version = "0.2.69" 1872 | source = "registry+https://github.com/rust-lang/crates.io-index" 1873 | checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" 1874 | 1875 | [[package]] 1876 | name = "web-sys" 1877 | version = "0.3.46" 1878 | source = "registry+https://github.com/rust-lang/crates.io-index" 1879 | checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" 1880 | dependencies = [ 1881 | "js-sys", 1882 | "wasm-bindgen", 1883 | ] 1884 | 1885 | [[package]] 1886 | name = "winapi" 1887 | version = "0.3.9" 1888 | source = "registry+https://github.com/rust-lang/crates.io-index" 1889 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1890 | dependencies = [ 1891 | "winapi-i686-pc-windows-gnu", 1892 | "winapi-x86_64-pc-windows-gnu", 1893 | ] 1894 | 1895 | [[package]] 1896 | name = "winapi-i686-pc-windows-gnu" 1897 | version = "0.4.0" 1898 | source = "registry+https://github.com/rust-lang/crates.io-index" 1899 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1900 | 1901 | [[package]] 1902 | name = "winapi-x86_64-pc-windows-gnu" 1903 | version = "0.4.0" 1904 | source = "registry+https://github.com/rust-lang/crates.io-index" 1905 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1906 | 1907 | [[package]] 1908 | name = "wio" 1909 | version = "0.2.2" 1910 | source = "registry+https://github.com/rust-lang/crates.io-index" 1911 | checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" 1912 | dependencies = [ 1913 | "winapi", 1914 | ] 1915 | 1916 | [[package]] 1917 | name = "xi-unicode" 1918 | version = "0.2.1" 1919 | source = "registry+https://github.com/rust-lang/crates.io-index" 1920 | checksum = "e71b85d8b1b8bfaf4b5c834187554d201a8cd621c2bbfa33efd41a3ecabd48b2" 1921 | 1922 | [[package]] 1923 | name = "xmlparser" 1924 | version = "0.13.3" 1925 | source = "registry+https://github.com/rust-lang/crates.io-index" 1926 | checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8" 1927 | 1928 | [[package]] 1929 | name = "xmlwriter" 1930 | version = "0.1.0" 1931 | source = "registry+https://github.com/rust-lang/crates.io-index" 1932 | checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" 1933 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "qc" 3 | version = "0.1.0" 4 | authors = ["Greg Morenz "] 5 | edition = "2018" 6 | 7 | [lib] 8 | path = "src/chess.rs" 9 | 10 | [dependencies] 11 | aljabar = "1.0.2" 12 | # Only used for gui... 13 | druid = { git = "https://github.com/gmorenz/druid", branch="gregs-changes", features = ["im", "svg"] } 14 | im = "15.0.0" 15 | 16 | [[bin]] 17 | name = "qc-term" 18 | path = "src/term.rs" 19 | 20 | [[bin]] 21 | name = "qc-druid" 22 | path = "src/druid.rs" 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Quantum Chess 2 | 3 | ![screenshot](screenshot.png) 4 | 5 | A somewhat hacky implementation of [this paper](https://arxiv.org/pdf/1906.05836.pdf) (made in a week over a holiday). 6 | 7 | It's not heavily tested and probably has some bugs still lying around, but it works reasonably well. 8 | 9 | Out of respect for the [attempt to monitize](https://quantumchess.net/) quantum chess by the people who invented the rules, `chess.rs`, `term.rs`, and `druid.rs` are (temporarily?) *not* publicly licensed apart from the license required by githubs TOS. That means you can clone and fork this repo (github's TOS), you can build and privately run the executable in most jurisdictions (that's just not copyright infringment), you can not publicly perform the work, redistribute those files, or do most other things with them. 10 | 11 | `gates.rs` `qcomp.rs`, and `qcomp_explainable.rs` are released under both MIT and Apache2 licenses, to match up with the rest of the rust ecosystem. 12 | 13 | The svg files were borrowed from [wikimedia commons](https://commons.wikimedia.org/wiki/Category:SVG_chess_pieces) and are licensed CC-BY-SA-3.0. 14 | 15 | # Usage 16 | 17 | Install [rustup](https://rustup.rs/), then 18 | 19 | ``` 20 | git clone https://github.com/gmorenz/qc 21 | cd qc 22 | rustup override set nightly-2020-12-23 23 | 24 | # If you have awhile (8m on my desktop), a few gb of memory, and want a much smaller much faster binary 25 | cargo run --bin qc-druid --release -- 10 # 10 is the seed for the random number generator, use any positive integer < 2^64. 26 | # If you're impatient (1-2m) or don't have quite that much memory to build 27 | cargo run --bin qc-druid -- 10 28 | ``` 29 | 30 | Note that it will take a few minutes to build, this is primarily because of alloc_qubit (see theoretical roadmap for more information). 31 | 32 | ## Controls and Rules 33 | 34 | Read the paper for the full rules, but here's the short and only approximately correct version: 35 | 36 | - Normal chess moves are legal moves, left click on the piece you want to move and left click on the square you want to go to. 37 | - Non pawns can "split" into two copies each with 50% probability of being there, left click on the piece you want to move, right click on the first square you want to go to, left click on the second square you want to go to. 38 | - Non pawns can "merge" two squares possibly containing the same kind of piece into one square, right click on the first square you want to merge, left click on the second square you want 39 | to merge, and left click on where you want to merge to (and note this still has to be a legal move!). 40 | - You cannot capture a piece when splitting or merging a piece, or move onto one of your own pieces of a different type. 41 | - Each square can only contain a single type/color of piece, if you make a move that would violate this, we will make a measurement of some kind to find out which piece is actually in a square as part of your move. 42 | 43 | The circles around pieces represent the probability of that piece being there. When you click on a piece we draw smaller circles representing the probability of each piece being there given the piece that you clicked on is actually there. 44 | 45 | The question of whether or not a square is occupied is quantum in quantum chess, everything else (piece type, whose turn it is, can you castle, etc) is classical. A quantum state^1 is a vector 2^(# qubits) dimensional complex vector. The n^th element of this vector represents the state where the (q)bits that are '1' in 'n' are true, and the (q)bits that are `0` in `n` are false. In our case the first 64 qubits represent the board's occupancy, so for example the 0xff_ff_00_00_00_00_ff_ff^th element represents the "starting state" where nothing has moved. The squared magnitude of the complex number in the element is the probability that we are in that state, and the direction is the "phase" which matters when we try and add up phases (commonly seen in merge moves!). At the bottom of the window is a series of boards representing these vector elements. Every non-zero element has a board, and top of it is a line in a circle representing the complex number. If that doesn't make sense, play around a bit and see if you can figure out what I mean, and if that doesn't work feel free to shoot me an email (I'm struggling to make this explanation both digestible and not assume too much knowledge of quantum computing). 46 | 47 | If you run the game from a terminal and watch the output you can see a bit of a verbose description of what is happening - notably including long form descriptions of what is being measured and the probability of measurements (both of which I would like to include in the UI in some formgiven more time to work on this). 48 | 49 | ^1 We only discuss "pure states" here. 50 | 51 | ## (Arguable) differences from the paper 52 | 53 | Split and merge moves are implemented in the form of "controlled unitaries" controlled by path ancilla qubits 54 | made up of the path between the destination and the source. I made the choice to never include any of the squares 55 | directly involved in the move on the path. I.e. in `e2^e4e6` the path from `e2` to `e6` is just `e3` and `e5`. 56 | This appears to match the https://quantumchess.net/play/ implementation, and makes merge moves act more like 57 | I expect. 58 | 59 | ## Differences from the https://quantumchess.net/play/ implementation 60 | 61 | The https://quantumchess.net/play implementation appears to implement additional move legality checking, which 62 | checks that a move could possibly complete before allowing it. For example after `g1^f3h3`, `d1f3` is still 63 | disallowed because it is moving through a (definitely there) pawn. In this implementation (and in my reading of 64 | the paper) `d1f3` is allowed because it is a legal chess move for the queen on `d1`, and it changes the state 65 | when it measures whether or not `f3` is present. This is suboptimal from a game design perspective, and if I 66 | was going to spend more time on this I might consider rule changes to check whether there is any possibility 67 | of the move completing before measuring `f3`. However I would probably not go with the https://quantumchess.net/play 68 | implementation because it does not check this completely, e.g. the following sequence of moves (all for white) 69 | measures the rook on a4 on the last move despite the d1a4 move not being able to sucesfully complete: 70 | `a2a4, a4a5, a1^a3a4, c2c4, g1f3, f3d4, d4^b3c2, d1a4.m1` ![board image](quantumchess.net.unneeded-measure.png). 71 | 72 | The https://quantumchess.net/play implementation appears to make a different measurement than me at the end of 73 | the move sequence `e2e4 e7e5 f1^d3c4 b8^a6c6 d3a6.m?` - perhaps somehow "knowing" that the two bishops 74 | resulting from the `f1^d3c4` split can't collide when making the decision of what to measure? It seems to me 75 | that my implementation matches the paper. 76 | 77 | # Source Code Tour 78 | 79 | Note that this was as much an excuse to experiment with const-generics and druid as anything else, which is reflected in the code a fair bit. 80 | 81 | There are four files 82 | 83 | - qcomp.rs: This is our bespoke quantum computer simulator, it should be reasonably efficient and fast. Quantum states are represented as a `HashMap`. We make heavy use of rust's experimental const generics to make it so that `BasisVector` is stored efficiently and does not need to allocate (though in reality we could probably achieve similar performance by just storing it as a `u256`) - moreover every function is monomorphized for the number of qbits/length of `BasisVector` which should lead to very fast code generated by the compiler. The tradeoff we pay for this is long compile times, a bit of awkward boiler plate to call the functions, and that we have to explicitly list out each number of qubits supported (in the definition of `with_bound_const`). We also have some qubit allocation/deallocation code here that should maybe be refactored out. 84 | 85 | - qcomp_explainable.rs: This file exists to help track what qubits in `qcomp.rs` mean, and generally be less "pure" about avoiding allocations and hyper optimized code. We also take advantage of it to hide some of the weird boiler plate generated by `qcomp.rs`. 86 | 87 | - gates.rs: This is a library of unitaries. 88 | 89 | - chess.rs: This implements the rules of quantum chess. `apply_move` is the main coordinator function and would be a good place to start reading. We take a `Move`, check that it is legal, map it to a `QuantumMove` (i.e. a kind of transformation to apply), apply it, check if it changed anything, and if it did update the remaining classical game state. 90 | 91 | - druid.rs: This implements a GUI for quantum chess using the druid gui framework (which is not production ready, but is pretty nice). 92 | 93 | - term.rs: This implements a terminal interface for quantum chess. 94 | 95 | # Theoretical Roadmap 96 | 97 | I don't really anticipate coming back to this project and further improving it - it was just a week long diversion over the holiday's. If I do though there are a number of things that could be improved: 98 | 99 | ## Just Programming 100 | 101 | - Code cleanliness - this was written fast and hacky - there's a lot that could be improved 102 | 103 | ### Chess Related 104 | 105 | - An option for exact arithmetic, this would be much slower (in the general case it requires allocation, I might try and use pointer packing tricks to avoid allocation in the common case though). 106 | - An undo button, there are two general approaches to this: 107 | 1. Rerun all the moves from the start and force measurements to measure the same way 108 | 2. Invert gates, and store "thrown out" measured states 109 | The first is slow, the second suffers from accuracy problems and could bloat memory usage in long games. Either could work. 110 | - Show fully captured pieces in the list of captured pieces 111 | - Add in UI for promoting to pieces other than pawns. 112 | - Make it illegal to move a pawn to the 8th row and *not* promote. 113 | - Only show ".m0"/".m1" when any other measurement was actually a possibility. 114 | 115 | ### Just qcomp related 116 | 117 | - The interface for controlled matricies is really clunky (but concise), and not as efficient as I'd like for huge states (wasting comparisons). Consider moving the control part to some sort of generic parameter. 118 | - Figure out a benchmark, for "reasonably computational-basis-aligned states" I think this should be very fast 119 | - Pass in matricies as const generic parametrs, it is my belief (that I would want to see benchmarks on) that this would lead to significantly faster code. 120 | - Reduce quadratic code size (in #qbits) to linear, specifically `alloc_qubit` is currently quadratic and could probably be linear with some clever use of specialization (I believe everything else is already linear). 121 | - Split this out into a seperate crate. 122 | 123 | ## Higher level work 124 | 125 | - The UI around selecting squares could be refined significantly, probably split into an "analysis" and a "move" mode, and disallow selections that don't lead to a legal move in the "move" mode. 126 | - Some form of description (in the UI) of what happened on any given move, maybe something like showing a quantum circuit diagram, or a table of how effected qubits were changed depending on their occupancy, in the top right, selectable by clicking on a move in the move history. Also nice would be the ability to view old positions/list of states. 127 | - A better way of visualizing "phase" for users. 128 | - Modify the move rules to only measure when it is actually necessary (not, e.g., on fully blocked moves). 129 | - More complete "garbage collection" for qubits: 130 | 131 | We have a concept of "freed" qubits, which lists qubits which we don't change anymore and allows the implementation to re-use their memory once they are no longer needed to describe the state of the non-free qubits (note that we only use pure states). We don't do a very good job of actually detecting when they are no longer needed however - we only detect that if they are always 0 or always 1 (in every state). 132 | 133 | We should also look for groups of free qubits that are now "factorizable" from the remaining qubits, at which point we could re-use all of those qubits after zeroing them in the bv. 134 | 135 | We should also detect when two free qubits of the same type (e.g. "Captured Black Queen") are redundant and can be merged. 136 | For instance if the state no longer depends on whether the queen was captured on move a or move b, but it does depend on whether or not it was captured, we would like to merge the "captured" qubits from move a and move b into a single qubit. 137 | - AI? There are two plausible approaches here that I have vaguely considered. Something like mu zero that doesn't need a full representation of the game and feeding it a view similar to the UI, or something like alpha zero and running it on randomly selected basis vectors from the state. The latter is probably slower, but given enough time might become more accurate as it is better able to "see" enanglement (it is still missing phase though). 138 | -------------------------------------------------------------------------------- /quantumchess.net.unneeded-measure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmorenz/qc/01a8646d8f10a0bcd122938e8beefec31a06b2ae/quantumchess.net.unneeded-measure.png -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmorenz/qc/01a8646d8f10a0bcd122938e8beefec31a06b2ae/screenshot.png -------------------------------------------------------------------------------- /src/Chess_bdt45.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Chess_blt45.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Chess_kdt45.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Chess_klt45.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Chess_ndt45.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 11 | 14 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Chess_nlt45.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 11 | 14 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Chess_pdt45.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/Chess_plt45.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/Chess_qdt45.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Chess_qlt45.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Chess_rdt45.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 11 | 14 | 17 | 20 | 23 | 26 | 29 | 32 | 35 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/Chess_rlt45.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 11 | 14 | 16 | 19 | 21 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/chess.rs: -------------------------------------------------------------------------------- 1 | #![allow(incomplete_features)] 2 | #![feature( 3 | backtrace, 4 | const_generics, 5 | const_evaluatable_checked, 6 | const_panic, 7 | array_map, 8 | hash_drain_filter, 9 | trait_alias, 10 | format_args_capture 11 | )] 12 | 13 | #[macro_use] pub mod qcomp; 14 | mod qcomp_explainable; 15 | mod gates; 16 | 17 | use qcomp::{QuantumState, BasisVector}; 18 | use qcomp_explainable::SimpleQuantumState; 19 | use std::fmt; 20 | 21 | #[macro_export] 22 | macro_rules! with_const { 23 | ($name:ident : $ty:ty = $val:expr; $($cval:literal)* $expr:block) => { 24 | match $val { 25 | $( 26 | $cval => { 27 | const $name: $ty = $cval; 28 | $expr 29 | } 30 | ),* 31 | _ => panic!("Unexpected value in with_const") 32 | } 33 | } 34 | } 35 | 36 | pub struct ChessState { 37 | pub occupancy: SimpleQuantumState, 38 | pub pieces: [Option; 64], 39 | 40 | pub is_white_turn: bool, 41 | // [[white kingside, white queenside], [black kingside, black queenside]] 42 | pub castling: [[bool; 2]; 2], 43 | pub en_passant_file: Option, 44 | pub move_number: u16, 45 | } 46 | 47 | // TODO: Move druid::Data behind a cfg gate or something, also this sucks 48 | #[derive(Copy, Clone, PartialEq, druid::Data)] 49 | pub enum Move { 50 | Simple{ from: Square, to: Square }, 51 | Split{ from: Square, to_1: Square, to_2: Square }, 52 | Merge{ from_1: Square, from_2: Square, to: Square }, 53 | Promote{ from: Square, to: Square, kind: PieceKind }, 54 | } 55 | 56 | enum MovementType { 57 | Jumps, Slides, Pawn 58 | } 59 | 60 | #[derive(Debug)] 61 | enum QuantumMove { 62 | // Optimization potential: Vec is at most 6 long, move fits in a u8, 63 | // much better to keep this as a inline list... 64 | Simple{ from: Square, to: Square, path: Vec }, 65 | Blocked{ from: Square, to: Square, path: Vec }, 66 | Capture{ from: Square, to: Square, path: Vec }, 67 | 68 | Split{ from: Square, to_1: Square, to_2: Square, path_1: Vec, path_2: Vec }, 69 | Merge{ from_1: Square, from_2: Square, to: Square, path_1: Vec, path_2: Vec }, 70 | 71 | // Pawns are weird 72 | PawnCapture{ from: Square, to: Square }, 73 | EnPassant{ from: Square, to: Square }, 74 | BlockedEnPassant{from: Square, to: Square}, 75 | CaptureEnPassant{from: Square, to: Square}, 76 | 77 | // Castling 78 | KingCastle, 79 | QueenCastle, 80 | } 81 | 82 | #[derive(Clone, Copy, PartialEq, Eq)] 83 | pub struct Piece { 84 | pub is_white: bool, 85 | pub kind: PieceKind, 86 | } 87 | impl Piece { 88 | pub fn symbol(self) -> char { 89 | use PieceKind::*; 90 | match (self.is_white, self.kind) { 91 | (true, Pawn) => '♙', 92 | (true, Rook) => '♖', 93 | (true, Knight) => '♘', 94 | (true, Bishop) => '♗', 95 | (true, Queen) => '♕', 96 | (true, King) => '♔', 97 | (false, Pawn) => '♟', 98 | (false, Rook) => '♜', 99 | (false, Knight) => '♞', 100 | (false, Bishop) => '♝', 101 | (false, Queen) => '♛', 102 | (false, King) => '♚', 103 | } 104 | } 105 | 106 | 107 | pub fn symbol_black(self) -> char { 108 | use PieceKind::*; 109 | match self.kind { 110 | Pawn => '♟', 111 | Rook => '♜', 112 | Knight => '♞', 113 | Bishop => '♝', 114 | Queen => '♛', 115 | King => '♚', 116 | } 117 | } 118 | } 119 | 120 | // TODO: Move druid::Data behind a cfg gate or something, also this sucks 121 | #[derive(Clone, Copy, PartialEq, Eq, Debug, druid::Data)] 122 | pub enum PieceKind { 123 | Pawn, 124 | Knight, 125 | Bishop, 126 | Rook, 127 | King, 128 | Queen, 129 | } 130 | 131 | 132 | // TODO: Move druid::Data behind a cfg gate or something, also this sucks 133 | #[derive(Copy, Clone, PartialEq, Eq, druid::Data)] 134 | pub struct Square { 135 | #[data(same_fn = "PartialEq::eq")] 136 | pub col: Column, 137 | #[data(same_fn = "PartialEq::eq")] 138 | pub row: Row, 139 | } 140 | 141 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 142 | pub enum Column { 143 | A, B, C, D, E, F, G, H 144 | } 145 | 146 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 147 | pub enum Row { 148 | R1, R2, R3, R4, R5, R6, R7, R8 149 | } 150 | 151 | impl ChessState { 152 | pub fn new_game() -> ChessState { 153 | let mut bv = qcomp::BasisVector::<64>::zero(); 154 | let mut pieces = [None; 64]; 155 | for &color in &[true, false] { 156 | for &row_perspective in &[Row::R1, Row::R2] { 157 | // perspective is it's own inverse. 158 | let row = row_perspective.perspective(color); 159 | use Column::*; 160 | use PieceKind::*; 161 | for &(col, piece_kind) in &[ 162 | (A, Rook), 163 | (B, Knight), 164 | (C, Bishop), 165 | (D, Queen), 166 | (E, King), 167 | (F, Bishop), 168 | (G, Knight), 169 | (H, Rook) 170 | ] { 171 | let square = Square{ row, col }; 172 | bv.set(square.get_idx(), true); 173 | let kind = 174 | if row_perspective == Row::R1 { piece_kind } 175 | else{ PieceKind::Pawn }; 176 | pieces[square.get_idx()] = Some(Piece { 177 | is_white: color, 178 | kind: kind, 179 | }); 180 | } 181 | } 182 | } 183 | 184 | ChessState { 185 | occupancy: SimpleQuantumState::new(bv, |i| format!("{}", Square::from_idx(i).unwrap())), 186 | pieces, 187 | is_white_turn: true, 188 | castling: [[true, true], [true, true]], 189 | en_passant_file: None, 190 | move_number: 0, 191 | } 192 | } 193 | 194 | /// Returns Result 195 | /// and mutates the state on Ok. 196 | pub fn apply_move(&self, chess_move: Move) -> Result< 197 | (ChessState, Option), 198 | String 199 | > { 200 | fn remove_zero_prob_pieces( 201 | state: &QuantumState, 202 | pieces: &mut [Option; 64] 203 | ) 204 | where [u8; (N + 7)/8]: { 205 | let mut used = BasisVector::<64>::zero(); 206 | for (bv, _) in state.iter() { 207 | used |= bv.resize::<64>(); 208 | } 209 | for i in 0.. 64 { 210 | if !used[i] { 211 | pieces[i] = None; 212 | } 213 | } 214 | } 215 | 216 | if self.is_legal(chess_move) { 217 | let qmove = self.get_qmove(chess_move); 218 | println!("\nMove is a {:?}", qmove); 219 | let (occupancy, mut pieces, measurement) = self.compute_move(qmove); 220 | if &self.occupancy.state == &occupancy.state { 221 | Err("State didn't change, try again".into()) 222 | } 223 | else { 224 | // En passant 225 | let en_passant_file = if let Move::Simple{from, to} = chess_move { 226 | if from.row.perspective(self.is_white_turn) == Row::R2 227 | && to.row.perspective(self.is_white_turn) == Row::R4 228 | && self.pieces[from.get_idx()].map(|piece| piece.kind) == Some(PieceKind::Pawn) { 229 | Some(from.col) 230 | } else { None } 231 | } else { None }; 232 | 233 | // Promotion 234 | if let Move::Promote{to, kind, ..} = chess_move { 235 | if pieces[to.get_idx()] == Some(Piece{ kind: PieceKind::Pawn, is_white: self.is_white_turn }) { 236 | pieces[to.get_idx()] = Some(Piece{ kind, is_white: self.is_white_turn }); 237 | } 238 | } 239 | 240 | let mut new_state = ChessState { 241 | occupancy, 242 | pieces, 243 | 244 | is_white_turn: !self.is_white_turn, 245 | castling: chess_move.update_castling(self.is_white_turn, self.castling), 246 | // TODO: 247 | en_passant_file, 248 | move_number: self.move_number + 1, 249 | }; 250 | apply_monomorphized_fn!(&new_state.occupancy.state, remove_zero_prob_pieces, &mut new_state.pieces); 251 | Ok((new_state, measurement)) 252 | } 253 | } 254 | else { 255 | Err("Illegal move".into()) 256 | } 257 | } 258 | 259 | fn get_qmove(&self, chess_move: Move) -> QuantumMove { 260 | use QuantumMove::*; 261 | use MovementType::{Slides, Jumps, Pawn as PawnMove}; 262 | let kind = self.get_moves_piece_kind(chess_move); 263 | 264 | // Castling 265 | if kind == PieceKind::King { 266 | if let Move::Simple{ from, to } = chess_move { 267 | if from.col.dist(to.col) == 2 { 268 | if to.col == Column::G { 269 | return KingCastle 270 | } 271 | else { 272 | return QueenCastle 273 | } 274 | } 275 | } 276 | } 277 | 278 | // Every other type of move 279 | match (kind.movement_type(), chess_move) { 280 | // Simple moves 281 | (Jumps, Move::Simple{ from, to }) => { 282 | if self.pieces[from.get_idx()] == self.pieces[to.get_idx()] { 283 | return Simple{ from, to, path: vec![] } 284 | } 285 | 286 | match self.get_relative_team(to) { 287 | Some(true) => Blocked{ from, to, path: vec![] }, 288 | Some(false) => Capture{ from, to, path: vec![] }, 289 | None => Simple{ from, to, path: vec![] }, 290 | } 291 | }, 292 | (Slides, Move::Simple{ from, to }) => { 293 | let path = from.path(to, None); 294 | if self.pieces[from.get_idx()] == self.pieces[to.get_idx()] { 295 | return Simple{ from, to, path } 296 | } 297 | 298 | match self.get_relative_team(to) { 299 | Some(true) => Blocked{ from, to, path }, 300 | Some(false) => Capture{ from, to, path }, 301 | None => Simple{ from, to, path }, 302 | } 303 | }, 304 | (PawnMove, Move::Simple{ from, to }) => { 305 | let path = from.path(to, None); 306 | if self.pieces[from.get_idx()] == self.pieces[to.get_idx()] { 307 | return Simple{ from, to, path } 308 | } 309 | 310 | let is_en_passant = 311 | from.row.perspective(self.is_white_turn) == Row::R5 312 | && from.col.dist(to.col) == 1 313 | && Some(to.col) == self.en_passant_file; 314 | let is_capture = from.col != to.col; 315 | let team = self.get_relative_team(to); 316 | match (is_capture, is_en_passant, team) { 317 | (false, false, None) => Simple{ from, to, path }, 318 | (false, false, Some(_)) => Blocked{ from, to, path }, 319 | (true, false, Some(false)) => PawnCapture{ from, to }, 320 | (true, true, None) => EnPassant{ from, to }, 321 | (true, true, Some(true)) => BlockedEnPassant{ from, to }, 322 | (true, true, Some(false)) => CaptureEnPassant{ from, to }, 323 | // Error checking for completeness 324 | (false, true, _) => panic!("en_passsant must capture"), 325 | (true, false, _) => panic!("Must be capturing a piece") 326 | } 327 | } 328 | 329 | // Split moves 330 | (Jumps, Move::Split{ from, to_1, to_2 }) => 331 | Split { from, to_1, to_2, path_1: vec![], path_2: vec![] }, 332 | (Slides, Move::Split{ from, to_1, to_2 }) => { 333 | let path_1 = from.path(to_1, Some(to_2)); 334 | let path_2 = from.path(to_2, Some(to_1)); 335 | Split{ from, to_1, to_2, path_1, path_2 } 336 | }, 337 | 338 | // Merge moves 339 | (Jumps, Move::Merge{ from_1, from_2, to }) => 340 | Merge{ from_1, from_2, to, path_1: vec![], path_2: vec![] }, 341 | (Slides, Move::Merge{ from_1, from_2, to }) => { 342 | let path_1 = from_1.path(to, Some(from_2)); 343 | let path_2 = from_2.path(to, Some(from_1)); 344 | Merge{ from_1, from_2, to, path_1, path_2 } 345 | }, 346 | 347 | // Promotion 348 | // Note: Promotion happens as a standard pawn quantum move, 349 | // and then a seperate promotion after the fact. 350 | (PawnMove, Move::Promote{ from, to, .. }) => { 351 | match (from.col == to.col, self.get_type(to).is_none()) { 352 | (true, true) => Simple{ from, to, path: vec![] }, 353 | (true, false) => Blocked{ from, to, path: vec![] }, 354 | (false, true) => unreachable!(), 355 | (false, false) => Capture{ from, to, path: vec![] }, 356 | } 357 | }, 358 | 359 | // Trying to merge/split a pawn, or promote a non pawn. 360 | // These are caught by is_illegal so shouldn't happen here. 361 | _ => unreachable!() 362 | } 363 | } 364 | 365 | // Note: *Very* large function despite it's apparently only reasonably large size, 366 | // we're using macros here to expand the line count by roughly 49 times! 367 | // If codegen has an issue, that's *probably* why. 368 | fn compute_move(&self, qmove: QuantumMove) -> (SimpleQuantumState, [Option; 64], Option) { 369 | use QuantumMove::*; 370 | match qmove { 371 | Simple{ from, to, path } => { 372 | let gate = gates::iswap(); 373 | let indicies = [from.get_idx(), to.get_idx()]; 374 | let occupancy = with_const!(LEN: usize = path.len(); 0 1 2 3 4 5 6 { 375 | let mut path_idx = [0; LEN]; 376 | for i in 0.. LEN { 377 | path_idx[i] = path[i].get_idx(); 378 | } 379 | self.occupancy.apply_controlled_unitaries_and_clone( 380 | [(&gate, None, &[], &path_idx)], 381 | indicies, 382 | ) 383 | }); 384 | 385 | let mut pieces = self.pieces; 386 | pieces[to.get_idx()] = pieces[from.get_idx()]; 387 | 388 | (occupancy, pieces, None) 389 | } 390 | Blocked{ from, to, path } => { 391 | // Temporary ancilla for measurement, false only if we failed to move 392 | // *because* we were blocked by a piece in target. 393 | let (mut occupancy, not_blocked) = self.occupancy.alloc_qubit_and_clone( 394 | None, 395 | format!("Block_{from}{to}"), 396 | format!("Ancilla qbit to measure whether {to} is occupied blocking {from}{to}") 397 | ); 398 | occupancy 399 | .apply_controlled_unitaries( 400 | [(&gates::pauli_x(), None, &[], &[to.get_idx()])], 401 | [not_blocked], 402 | ); 403 | let bit_not_blocked = occupancy.measure(not_blocked); 404 | occupancy.free_qubit(not_blocked); 405 | let mut pieces = self.pieces; 406 | if bit_not_blocked { 407 | with_const!(LEN: usize = path.len(); 0 1 2 3 4 5 6 { 408 | let mut path_idx = [0; LEN]; 409 | for i in 0.. LEN { 410 | path_idx[i] = path[i].get_idx(); 411 | } 412 | occupancy.apply_controlled_unitaries( 413 | [(&gates::iswap(), None, &[], &path_idx)], 414 | [from.get_idx(), to.get_idx()], 415 | ) 416 | }); 417 | pieces[to.get_idx()] = pieces[from.get_idx()]; 418 | } 419 | 420 | (occupancy, pieces, Some(bit_not_blocked)) 421 | } 422 | Capture{ from, to, path } => { 423 | // Temporary ancilla for measurement, true if either we sucesfully moved 424 | // or the target is unoccupied. 425 | let purpose = 426 | if path.len() == 0 { 427 | format!("Ancilla qbit to measure whether {from} is occupied so {from}{to} captures a piece") 428 | } else { 429 | let path_descr: String = path.iter().map(|v| format!("{} ", v)).collect::(); 430 | let path_descr = &path_descr[0.. path_descr.len() - 1]; 431 | format!("Ancilla qibt to measure whether either:\n 1. {from} is occupied and the path ({path_descr}) is not so the capture {from}{to} succeeds, or\n 2. The path ({path_descr}) is blocked but {to} is unocuppied so the capture didn't need to suceed") 432 | }; 433 | let (mut occupancy, still_there) = self.occupancy.alloc_qubit_and_clone( 434 | None, 435 | format!("CanCapture_{from}{to}"), 436 | purpose 437 | ); 438 | 439 | let bit = with_const!(LEN: usize = path.len(); 0 1 2 3 4 5 6 { 440 | let mut path_indicies = [0; LEN]; 441 | for i in 0.. LEN { 442 | path_indicies[i] = path[i].get_idx() 443 | } 444 | 445 | // TODO: Merge these calls for performance 446 | occupancy 447 | .apply_controlled_unitaries( 448 | [(&gates::pauli_x(), Some(&[from.get_idx()]), &[], &path_indicies)], 449 | [still_there], 450 | ); 451 | occupancy 452 | .apply_controlled_unitaries( 453 | [(&gates::pauli_x(), Some(&path_indicies), &[], &[to.get_idx()])], 454 | [still_there], 455 | ); 456 | 457 | let bit = occupancy.measure(still_there); 458 | occupancy.free_qubit(still_there); 459 | 460 | if bit { 461 | // Qubit that might stick arround for awhile, to represent whether or 462 | // not the piece was captured 463 | let sym = self.pieces[to.get_idx()].unwrap().symbol(); 464 | let is_captured = occupancy.alloc_qubit( 465 | self.pieces[to.get_idx()], 466 | format!("Captured{sym}_{from}{to}"), 467 | format!("Whether the {sym} formerly on {to} was captured") 468 | ); 469 | occupancy.apply_controlled_unitaries( 470 | [(&gates::iswap3_01_12(), None, &[], &path_indicies)], 471 | [is_captured, to.get_idx(), from.get_idx()] 472 | ); 473 | occupancy.free_qubit(is_captured); 474 | } 475 | 476 | bit 477 | }); 478 | 479 | let mut pieces = self.pieces; 480 | if bit { 481 | pieces[to.get_idx()] = pieces[from.get_idx()]; 482 | } 483 | 484 | (occupancy, pieces, Some(bit)) 485 | } 486 | 487 | Split{ from, to_1, to_2, path_1, path_2 } => { 488 | let indicies_split = [from.get_idx(), to_1.get_idx(), to_2.get_idx()]; 489 | 490 | let gate_split = gates::qc_split(); 491 | let gate_jmp_1 = gates::iswap3_01(); 492 | let gate_jmp_2 = gates::iswap3_02(); 493 | 494 | let occupancy = with_const!(LEN_2: usize = path_2.len(); 0 1 2 3 4 5 6 { 495 | let mut path_2_idx = [0; LEN_2]; 496 | for i in 0.. LEN_2 { 497 | path_2_idx[i] = path_2[i].get_idx(); 498 | } 499 | with_const!(LEN_1: usize = path_1.len(); 0 1 2 3 4 5 6 { 500 | let mut path_1_idx = [0; LEN_1]; 501 | let mut path_split_idx = [0; LEN_1 + LEN_2]; 502 | for i in 0.. LEN_1 { 503 | path_1_idx[i] = path_1[i].get_idx(); 504 | path_split_idx[i] = path_1[i].get_idx(); 505 | } 506 | for i in 0.. LEN_2 { 507 | path_split_idx[LEN_1 + i] = path_2_idx[i]; 508 | } 509 | 510 | self.occupancy.apply_controlled_unitaries_and_clone( 511 | [ 512 | ( 513 | &gate_split, 514 | None, 515 | &[], 516 | &path_split_idx as &[usize] 517 | ), 518 | ( 519 | &gate_jmp_1, 520 | Some(&path_2_idx as &[usize]), 521 | &[], 522 | &path_1_idx 523 | ), 524 | ( 525 | &gate_jmp_2, 526 | Some(&path_1_idx), 527 | &[], 528 | &path_2_idx 529 | ) 530 | ], 531 | indicies_split 532 | ) 533 | }) 534 | }); 535 | 536 | let mut pieces = self.pieces; 537 | pieces[to_1.get_idx()] = pieces[from.get_idx()]; 538 | pieces[to_2.get_idx()] = pieces[from.get_idx()]; 539 | 540 | (occupancy, pieces, None) 541 | } 542 | Merge{ from_1, from_2, to, path_1, path_2 } => { 543 | // let indicies_merge = [from_1.get_idx(), from_2.get_idx(), to.get_idx()]; 544 | let indicies_merge = [to.get_idx(), from_2.get_idx(), from_1.get_idx()]; 545 | 546 | let gate_merge = gates::qc_merge(); 547 | let gate_jmp_2 = gates::iswap3_01(); 548 | let gate_jmp_1 = gates::iswap3_02(); 549 | 550 | let occupancy = with_const!(LEN_2: usize = path_2.len(); 0 1 2 3 4 5 6 { 551 | let mut path_2_idx = [0; LEN_2]; 552 | for i in 0.. LEN_2 { 553 | path_2_idx[i] = path_2[i].get_idx(); 554 | } 555 | with_const!(LEN_1: usize = path_1.len(); 0 1 2 3 4 5 6 { 556 | let mut path_1_idx = [0; LEN_1]; 557 | let mut path_merge_idx = [0; LEN_1 + LEN_2]; 558 | for i in 0.. LEN_1 { 559 | path_1_idx[i] = path_1[i].get_idx(); 560 | path_merge_idx[i] = path_1[i].get_idx(); 561 | } 562 | for i in 0.. LEN_2 { 563 | path_merge_idx[LEN_1 + i] = path_2_idx[i]; 564 | } 565 | 566 | self.occupancy.apply_controlled_unitaries_and_clone( 567 | [ 568 | ( 569 | &gate_merge, 570 | None, 571 | &[], 572 | &path_merge_idx, 573 | ), 574 | ( 575 | &gate_jmp_1, 576 | Some(&path_2_idx), 577 | &[], 578 | &path_1_idx, 579 | ), 580 | ( 581 | &gate_jmp_2, 582 | Some(&path_1_idx), 583 | &[], 584 | &path_2_idx, 585 | ) 586 | 587 | ], 588 | indicies_merge 589 | ) 590 | }) 591 | }); 592 | 593 | let mut pieces = self.pieces; 594 | pieces[to.get_idx()] = pieces[from_1.get_idx()]; 595 | 596 | (occupancy, pieces, None) 597 | } 598 | 599 | PawnCapture{ from, to } => { 600 | let (mut occupancy, pawn_present) = self.occupancy.alloc_qubit_and_clone( 601 | None, 602 | format!("CanPawnCapture_{from}{to}"), 603 | format!("Ancilla qbit to measure whether {from} is actually there") 604 | ); 605 | occupancy.apply_controlled_unitaries( 606 | [(&gates::pauli_x(), Some(&[from.get_idx()]), &[], &[])], 607 | [pawn_present], 608 | ); 609 | let bit_present = occupancy.measure(pawn_present); 610 | occupancy.free_qubit(pawn_present); 611 | 612 | if bit_present { 613 | let sym = self.pieces[to.get_idx()].unwrap().symbol(); 614 | let captured = occupancy.alloc_qubit( 615 | self.pieces[to.get_idx()], 616 | format!("PawnCaptured{sym}_{from}{to}"), 617 | format!("Whether the {sym} formerly on {to} was captured") 618 | ); 619 | occupancy.apply_controlled_unitaries( 620 | // Mild deviation from the paper, which asks for both from and to, 621 | // but we only allow for "any" requirements here not all, and we 622 | // just measured from to be there, so this is equivalent. 623 | [(&gates::iswap3_01_12(), Some(&[to.get_idx()]), &[], &[])], 624 | [captured, to.get_idx(), from.get_idx()], 625 | ); 626 | occupancy.free_qubit(captured); 627 | let mut pieces = self.pieces; 628 | pieces[to.get_idx()] = pieces[from.get_idx()]; 629 | (occupancy, pieces, Some(true)) 630 | } 631 | else { 632 | (occupancy, self.pieces, Some(false)) 633 | } 634 | } 635 | 636 | KingCastle => { 637 | use Column::*; 638 | let row = Row::R1.perspective(self.is_white_turn); 639 | // king from, king to, rook from, rook to 640 | let sq_kf = Square{ row, col: E }; 641 | let sq_kt = Square{ row, col: G }; 642 | let sq_rf = Square{ row, col: H }; 643 | let sq_rt = Square{ row, col: F }; 644 | 645 | let (mut occupancy, can_castle) = self.occupancy.alloc_qubit_and_clone( 646 | None, 647 | format!("CanKingCastle"), 648 | format!("Ancilla qubit to measure whether or not castling suceeds") 649 | ); 650 | occupancy.apply_controlled_unitaries( 651 | [(&gates::pauli_x(), None, &[], &[sq_kt.get_idx(), sq_rt.get_idx()])], 652 | [can_castle], 653 | ); 654 | let can_castle_bit = occupancy.measure(can_castle); 655 | occupancy.free_qubit(can_castle); 656 | let mut pieces = self.pieces; 657 | if can_castle_bit { 658 | occupancy.apply_unitary( 659 | gates::iswap(), 660 | [sq_rf.get_idx(), sq_rt.get_idx()], 661 | ); 662 | occupancy.apply_unitary( 663 | gates::iswap(), 664 | [sq_kf.get_idx(), sq_kt.get_idx()], 665 | ); 666 | pieces[sq_rt.get_idx()] = pieces[sq_rf.get_idx()]; 667 | pieces[sq_kt.get_idx()] = pieces[sq_kf.get_idx()]; 668 | } 669 | 670 | (occupancy, pieces, Some(can_castle_bit)) 671 | } 672 | 673 | QueenCastle => { 674 | use Column::*; 675 | let row = Row::R1.perspective(self.is_white_turn); 676 | // king from, king to, rook from, rook to 677 | let sq_kf = Square{ row, col: E }; 678 | let sq_kt = Square{ row, col: C }; 679 | let sq_rf = Square{ row, col: A }; 680 | let sq_rt = Square{ row, col: D }; 681 | let sq_mid = Square{ row, col: B }; 682 | 683 | let (mut occupancy, can_castle) = self.occupancy.alloc_qubit_and_clone( 684 | None, 685 | format!("CanQueenCastle"), 686 | format!("Ancilla qubit to measure whether or not the squares we move to while castling are available") 687 | ); 688 | occupancy.apply_controlled_unitaries( 689 | [(&gates::pauli_x(), None, &[], &[sq_kt.get_idx(), sq_rt.get_idx()])], 690 | [can_castle], 691 | ); 692 | let can_castle_bit = occupancy.measure(can_castle); 693 | occupancy.free_qubit(can_castle); 694 | let mut pieces = self.pieces; 695 | if can_castle_bit { 696 | occupancy.apply_controlled_unitaries( 697 | [(&gates::iswap(), None, &[], &[sq_mid.get_idx()])], 698 | [sq_rf.get_idx(), sq_rt.get_idx()], 699 | ); 700 | occupancy.apply_controlled_unitaries( 701 | [(&gates::iswap(), None, &[], &[sq_mid.get_idx()])], 702 | [sq_kf.get_idx(), sq_kt.get_idx()], 703 | ); 704 | pieces[sq_rt.get_idx()] = pieces[sq_rf.get_idx()]; 705 | pieces[sq_kt.get_idx()] = pieces[sq_kf.get_idx()]; 706 | } 707 | 708 | (occupancy, pieces, Some(can_castle_bit)) 709 | } 710 | 711 | EnPassant{ from, to } => { 712 | let ep = Square{ 713 | col: to.col, 714 | row: Row::R5.perspective(self.is_white_turn), 715 | }; 716 | 717 | let sym = self.pieces[ep.get_idx()].unwrap().symbol(); 718 | let (mut occupancy, capture) = self.occupancy.alloc_qubit_and_clone( 719 | self.pieces[ep.get_idx()], 720 | format!("Captured{sym}_{from}{to}EnPassant"), 721 | format!("Whether the {sym} formerly on {ep} was captured via en passant") 722 | ); 723 | 724 | occupancy.apply_controlled_unitaries( 725 | [(&gates::iswap4_01_23(), None, &[from.get_idx(), ep.get_idx()], &[])], 726 | [from.get_idx(), to.get_idx(), ep.get_idx(), capture], 727 | ); 728 | 729 | let mut pieces = self.pieces; 730 | pieces[to.get_idx()] = pieces[from.get_idx()]; 731 | (occupancy, pieces, None) 732 | } 733 | 734 | BlockedEnPassant{ from, to } => { 735 | let ep = Square{ 736 | col: to.col, 737 | row: Row::R5.perspective(self.is_white_turn), 738 | }; 739 | 740 | let (mut occupancy, ancilla) = self.occupancy.alloc_qubit_and_clone( 741 | None, 742 | format!("EnPassantNotBlocked"), 743 | format!("Whether the piece on {to} does not block en passant") 744 | ); 745 | occupancy.apply_controlled_unitaries( 746 | [(&gates::pauli_x(), None, &[], &[to.get_idx()])], 747 | [ancilla] 748 | ); 749 | let can_enpassant = occupancy.measure(ancilla); 750 | 751 | let mut pieces = self.pieces; 752 | if can_enpassant { 753 | let sym = self.pieces[ep.get_idx()].unwrap().symbol(); 754 | let capture = occupancy.alloc_qubit( 755 | self.pieces[ep.get_idx()], 756 | format!("Captured{sym}_{from}{to}EnPassant"), 757 | format!("Whether the {sym} formerly on {ep} was captured via en passant") 758 | ); 759 | 760 | occupancy.apply_controlled_unitaries( 761 | [(&gates::iswap4_01_23(), None, &[from.get_idx(), ep.get_idx()], &[])], 762 | [from.get_idx(), to.get_idx(), ep.get_idx(), capture], 763 | ); 764 | 765 | pieces[to.get_idx()] = pieces[from.get_idx()]; 766 | } 767 | 768 | (occupancy, pieces, Some(can_enpassant)) 769 | } 770 | 771 | CaptureEnPassant{ from, to } => { 772 | let ep = Square{ 773 | col: to.col, 774 | row: Row::R5.perspective(self.is_white_turn), 775 | }; 776 | 777 | let (mut occupancy, ancilla) = self.occupancy.alloc_qubit_and_clone( 778 | None, 779 | format!("EnPassantFromThere"), 780 | format!("Whether the piece on {from} is actually there to execute the capture/en passant") 781 | ); 782 | occupancy.apply_controlled_unitaries( 783 | [(&gates::pauli_x(), None, &[from.get_idx()], &[])], 784 | [ancilla] 785 | ); 786 | let can_enpassant = occupancy.measure(ancilla); 787 | 788 | let mut pieces = self.pieces; 789 | if can_enpassant { 790 | let sym = self.pieces[ep.get_idx()].unwrap().symbol(); 791 | let capture_ep = occupancy.alloc_qubit( 792 | self.pieces[ep.get_idx()], 793 | format!("Captured{sym}_{from}{to}EnPassant"), 794 | format!("Whether the {sym} formerly on {ep} was captured via en passant") 795 | ); 796 | 797 | let sym = self.pieces[to.get_idx()].unwrap().symbol(); 798 | let capture = occupancy.alloc_qubit( 799 | self.pieces[to.get_idx()], 800 | format!("Captured{sym}_{from}{to}"), 801 | format!("Whether the {sym} formerly on {ep} was captured (instead of en passant)") 802 | ); 803 | 804 | occupancy.apply_controlled_unitaries( 805 | [(&gates::iswap5_43_21_10(), Some(&[to.get_idx(), ep.get_idx()]), &[], &[])], 806 | [from.get_idx(), to.get_idx(), capture, ep.get_idx(), capture_ep], 807 | ); 808 | 809 | pieces[to.get_idx()] = pieces[from.get_idx()]; 810 | } 811 | 812 | (occupancy, pieces, Some(can_enpassant)) 813 | } 814 | } 815 | } 816 | 817 | /// Checks that the pieces only move in "piece appropriate ways". 818 | /// 819 | /// Including: 820 | /// 821 | /// 1. Pawns only take pieces of the opposite color. 822 | /// 2. Pieces only move to squares they could move to in traditional chess on an empty board. 823 | /// 3. Castling rules. 824 | /// 4. It is the right turn for the moving piece to move. 825 | /// 826 | /// This *does not* include any checks on whether or not the piece moves 827 | /// through any other pieces, that happens later at a measurement phase. 828 | fn is_legal(&self, chess_move: Move) -> bool { 829 | match chess_move { 830 | Move::Simple{from, to } => { 831 | if from == to { 832 | return false; 833 | } 834 | 835 | let piece_kind = match self.get_type(from) { 836 | Some(Piece{kind, is_white}) if is_white == self.is_white_turn => { kind } 837 | _ => return false 838 | }; 839 | 840 | match piece_kind { 841 | PieceKind::Pawn => { 842 | // 4 legal types of moves. 843 | // 1. Moving forward 1 844 | // 2. Moving forward 2 if on second row 845 | // 3. Moving diagonal one if that square is of the opposing team 846 | // 4. en passant 847 | 848 | // Type 1 849 | if from.col == to.col 850 | && from.row.forwards(1, self.is_white_turn) == Some(to.row) { 851 | return true 852 | } 853 | 854 | // Type 2 855 | if from.col == to.col 856 | && from.row.perspective(self.is_white_turn) == Row::R2 857 | && from.row.forwards(2, self.is_white_turn) == Some(to.row) { 858 | return true 859 | } 860 | 861 | // Type 3 862 | if from.col.dist(to.col) == 1 863 | && from.row.forwards(1, self.is_white_turn) == Some(to.row) 864 | && self.get_team(to) == Some(!self.is_white_turn) { 865 | return true 866 | } 867 | 868 | // Type 4 869 | if dbg!(from.col.dist(to.col) == 1) 870 | && dbg!(from.row.perspective(self.is_white_turn) == Row::R5) 871 | && dbg!(to.row.perspective(self.is_white_turn) == Row::R6) 872 | && dbg!(Some(to.col) == self.en_passant_file) { 873 | println!("En passant"); 874 | return true 875 | } 876 | 877 | false 878 | } 879 | PieceKind::King => { 880 | // King Side Castling 881 | if from.col == Column::E && to.col == Column::G 882 | && self.can_king_side_castle() 883 | && from.row.perspective(self.is_white_turn) == Row::R1 884 | && to.row.perspective(self.is_white_turn) == Row::R1 { 885 | return true; 886 | } 887 | 888 | // Queen side castling 889 | if from.col == Column::E && to.col == Column::C 890 | && self.can_queen_side_castle() 891 | && from.row.perspective(self.is_white_turn) == Row::R1 892 | && to.row.perspective(self.is_white_turn) == Row::R1 { 893 | return true; 894 | } 895 | 896 | from.col.dist(to.col) <= 1 && from.row.dist(to.row) <= 1 897 | } 898 | PieceKind::Knight => { 899 | (from.col.dist(to.col) == 2 && from.row.dist(to.row) == 1) || 900 | (from.row.dist(to.row) == 2 && from.col.dist(to.col) == 1) 901 | } 902 | PieceKind::Bishop => { 903 | from.col.dist(to.col) == from.row.dist(to.row) 904 | } 905 | PieceKind::Rook => { 906 | (from.col.dist(to.col) == 0 && from.row.dist(to.row) != 0) || 907 | (from.row.dist(to.row) == 0 && from.col.dist(to.col) != 0 ) 908 | } 909 | PieceKind::Queen => { 910 | (from.col.dist(to.col) == from.row.dist(to.row)) || 911 | (from.col.dist(to.col) == 0 && from.row.dist(to.row) != 0) || 912 | (from.row.dist(to.row) == 0 && from.col.dist(to.col) != 0 ) 913 | } 914 | } 915 | } 916 | Move::Promote{from, to, kind} => { 917 | if from == to { 918 | return false; 919 | } 920 | // Check we are moving to the last row. 921 | if to.row.perspective(self.is_white_turn) != Row::R8 { 922 | return false; 923 | } 924 | // Check we are moving a pawn 925 | match self.get_type(from) { 926 | Some(Piece{ kind: PieceKind::Pawn, .. }) => (), 927 | _ => return false, 928 | }; 929 | // Check that we aren't promoting to a pawn 930 | if kind == PieceKind::Pawn { 931 | return false; 932 | } 933 | // Check it's an otherwise legal move 934 | self.is_legal(Move::Simple{from, to}) 935 | } 936 | Move::Split{ from, to_1, to_2 } => { 937 | if from == to_1 || from == to_2 || to_1 == to_2 { 938 | return false; 939 | } 940 | let from_type = match self.get_type(from) { 941 | Some(t) => t, 942 | None => return false, 943 | }; 944 | 945 | from_type.kind != PieceKind::Pawn 946 | && self.get_type(to_1).unwrap_or(from_type) == from_type 947 | && self.get_type(to_2).unwrap_or(from_type) == from_type 948 | && self.is_legal(Move::Simple{from, to: to_1}) 949 | && self.is_legal(Move::Simple{from, to: to_2}) 950 | } 951 | Move::Merge{ from_1, from_2, to } => { 952 | if from_1 == from_2 || from_1 == to || from_2 == to { 953 | return false; 954 | } 955 | let from_type = match self.get_type(from_1) { 956 | Some(t) => t, 957 | None => return false, 958 | }; 959 | 960 | from_type.kind != PieceKind::Pawn 961 | && self.get_type(from_2) == Some(from_type) 962 | && self.get_type(to).unwrap_or(from_type) == from_type 963 | && self.is_legal(Move::Simple{from: from_1, to}) 964 | && self.is_legal(Move::Simple{from: from_2, to}) 965 | } 966 | } 967 | } 968 | 969 | fn can_king_side_castle(&self) -> bool { 970 | self.castling[if self.is_white_turn { 0 } else { 1 }][0] 971 | } 972 | 973 | fn can_queen_side_castle(&self) -> bool { 974 | self.castling[if self.is_white_turn { 0 } else { 1 }][1] 975 | } 976 | 977 | // fn get_partition(&self, chess_move: Move) -> impl Fn(BasisVector<64>) -> bool { 978 | // let from = match chess_move { 979 | // Move::Simple{ from, .. } => Some(from), 980 | // Move::Promote{ from, .. } => Some(from), 981 | // _ => None 982 | // }; 983 | // move |bv| { 984 | // let from = match from { 985 | // // Quantum move, no partitioning/always succeeds 986 | // None => return true, 987 | // Some(from) => from 988 | // }; 989 | 990 | // let idx = from.get_idx(); 991 | // bv[from] 992 | // } 993 | // } 994 | 995 | fn get_type(&self, square: Square) -> Option { 996 | self.pieces[square.get_idx()] 997 | } 998 | 999 | fn get_moves_piece_kind(&self, chess_move: Move) -> PieceKind { 1000 | use Move::*; 1001 | let from = match chess_move { 1002 | Simple{ from, .. } 1003 | | Promote{ from, .. } 1004 | | Split{ from, .. } 1005 | | Merge { from_1: from, .. } => from 1006 | }; 1007 | self.pieces[from.get_idx()].map(|x| x.kind).expect("Get move piece kind only valid if move is legal") 1008 | } 1009 | 1010 | fn get_team(&self, square: Square) -> Option { 1011 | self.get_type(square).map(|x| x.is_white) 1012 | } 1013 | 1014 | fn get_relative_team(&self, square: Square) -> Option { 1015 | self.get_team(square).map(|x| x == self.is_white_turn) 1016 | } 1017 | } 1018 | 1019 | impl PieceKind { 1020 | fn movement_type(self) -> MovementType { 1021 | use PieceKind::*; 1022 | match self { 1023 | Bishop | Rook | Queen => MovementType::Slides, 1024 | Knight | King => MovementType::Jumps, 1025 | Pawn => MovementType::Pawn 1026 | } 1027 | } 1028 | } 1029 | 1030 | impl Square { 1031 | pub fn get_idx(self) -> usize { 1032 | let c = self.col.get_idx(); 1033 | let r = self.row.get_idx(); 1034 | 8 * r + c 1035 | } 1036 | 1037 | fn from_idx(idx: usize) -> Option { 1038 | let col = Column::from_idx(idx % 8).unwrap(); 1039 | Row::from_idx(idx / 8).map(|row| Square{ row, col }) 1040 | } 1041 | 1042 | fn path(self, to: Square, exclude: Option) -> Vec { 1043 | let mut cur_row = self.row.get_idx(); 1044 | let mut cur_col = self.col.get_idx(); 1045 | let mut out = vec![]; 1046 | let to_row = to.row.get_idx(); 1047 | let to_col = to.col.get_idx(); 1048 | loop { 1049 | if cur_row < to_row { cur_row += 1 } 1050 | if cur_row > to_row { cur_row -= 1 } 1051 | if cur_col < to_col { cur_col += 1 } 1052 | if cur_col > to_col { cur_col -= 1 } 1053 | if cur_row == to_row && cur_col == to_col { return out }; 1054 | let sq = Square{ 1055 | row: Row::from_idx(cur_row).unwrap(), 1056 | col: Column::from_idx(cur_col).unwrap(), 1057 | }; 1058 | if exclude == Some(sq) { continue }; 1059 | out.push(sq) 1060 | } 1061 | } 1062 | } 1063 | 1064 | impl Column { 1065 | fn dist(self, rhs: Column) -> usize { 1066 | (self.get_idx() as i32 - rhs.get_idx() as i32).abs() as usize 1067 | } 1068 | 1069 | pub fn get_idx(self) -> usize { 1070 | use Column::*; 1071 | match self { 1072 | A => 0, 1073 | B => 1, 1074 | C => 2, 1075 | D => 3, 1076 | E => 4, 1077 | F => 5, 1078 | G => 6, 1079 | H => 7, 1080 | } 1081 | } 1082 | 1083 | pub fn from_idx(idx: usize) -> Option { 1084 | use Column::*; 1085 | match idx { 1086 | 0 => Some(A), 1087 | 1 => Some(B), 1088 | 2 => Some(C), 1089 | 3 => Some(D), 1090 | 4 => Some(E), 1091 | 5 => Some(F), 1092 | 6 => Some(G), 1093 | 7 => Some(H), 1094 | _ => None 1095 | } 1096 | } 1097 | } 1098 | 1099 | impl Row { 1100 | fn dist(self, rhs: Row) -> usize { 1101 | (self.get_idx() as i32 - rhs.get_idx() as i32).abs() as usize 1102 | } 1103 | 1104 | pub fn get_idx(self) -> usize { 1105 | use Row::*; 1106 | match self { 1107 | R1 => 0, 1108 | R2 => 1, 1109 | R3 => 2, 1110 | R4 => 3, 1111 | R5 => 4, 1112 | R6 => 5, 1113 | R7 => 6, 1114 | R8 => 7, 1115 | } 1116 | } 1117 | 1118 | pub fn from_idx(idx: usize) -> Option { 1119 | use Row::*; 1120 | match idx { 1121 | 0 => Some(R1), 1122 | 1 => Some(R2), 1123 | 2 => Some(R3), 1124 | 3 => Some(R4), 1125 | 4 => Some(R5), 1126 | 5 => Some(R6), 1127 | 6 => Some(R7), 1128 | 7 => Some(R8), 1129 | _ => None 1130 | } 1131 | } 1132 | 1133 | fn forwards(self, n: usize, is_white: bool) -> Option{ 1134 | let idx = self.get_idx(); 1135 | if idx < n && !is_white { None } 1136 | else { 1137 | let new_idx = if is_white { n + idx } else { idx - n}; 1138 | Self::from_idx(new_idx) 1139 | } 1140 | } 1141 | 1142 | pub fn perspective(self, is_white: bool) -> Row { 1143 | if is_white{ self } 1144 | else { Self::from_idx(7 - self.get_idx()).unwrap() } 1145 | } 1146 | } 1147 | 1148 | impl Square { 1149 | fn parse(str: &mut &str) -> Result { 1150 | use Column::*; 1151 | use Row::*; 1152 | if str.len() < 2 { 1153 | return Err(format!("String '{}' is too short to specify a square", *str)); 1154 | } 1155 | let col = match str.as_bytes()[0] { 1156 | b'a' => A, 1157 | b'b' => B, 1158 | b'c' => C, 1159 | b'd' => D, 1160 | b'e' => E, 1161 | b'f' => F, 1162 | b'g' => G, 1163 | b'h' => H, 1164 | _ => return Err(format!("Invalid column: {}", str.chars().next().unwrap())) 1165 | }; 1166 | let row = match str.as_bytes()[1] { 1167 | b'1' => R1, 1168 | b'2' => R2, 1169 | b'3' => R3, 1170 | b'4' => R4, 1171 | b'5' => R5, 1172 | b'6' => R6, 1173 | b'7' => R7, 1174 | b'8' => R8, 1175 | _ => return Err(format!("Invalid column: {}", str.chars().next().unwrap())) 1176 | }; 1177 | *str = &str[2..]; 1178 | Ok(Square{ row, col }) 1179 | } 1180 | } 1181 | 1182 | impl Column { 1183 | fn get_char(self) -> char { 1184 | use Column::*; 1185 | match self { 1186 | A => 'a', 1187 | B => 'b', 1188 | C => 'c', 1189 | D => 'd', 1190 | E => 'e', 1191 | F => 'f', 1192 | G => 'g', 1193 | H => 'h', 1194 | } 1195 | } 1196 | } 1197 | 1198 | impl Row { 1199 | fn get_char(self) -> char { 1200 | use Row::*; 1201 | match self { 1202 | R1 => '1', 1203 | R2 => '2', 1204 | R3 => '3', 1205 | R4 => '4', 1206 | R5 => '5', 1207 | R6 => '6', 1208 | R7 => '7', 1209 | R8 => '8', 1210 | } 1211 | } 1212 | } 1213 | 1214 | impl fmt::Display for Piece { 1215 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 1216 | let mut buf = [0; 4]; 1217 | fmt.write_str(self.symbol().encode_utf8(&mut buf)) 1218 | } 1219 | } 1220 | impl fmt::Debug for Piece { 1221 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 1222 | let mut buf = [0; 4]; 1223 | fmt.write_str(self.symbol().encode_utf8(&mut buf)) 1224 | } 1225 | } 1226 | 1227 | impl fmt::Display for Square { 1228 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 1229 | fmt.write_fmt(format_args!("{}{}", self.col.get_char(), self.row.get_char())) 1230 | } 1231 | } 1232 | impl fmt::Debug for Square { 1233 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 1234 | fmt.write_fmt(format_args!("{}{}", self.col.get_char(), self.row.get_char())) 1235 | } 1236 | } 1237 | 1238 | impl fmt::Display for Move { 1239 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 1240 | use Move::*; 1241 | match *self { 1242 | Simple{ from, to } => { 1243 | fmt.write_fmt(format_args!("{from}{to}")) 1244 | }, 1245 | Split{ from, to_1, to_2 } => { 1246 | fmt.write_fmt(format_args!("{from}^{to_1}{to_2}")) 1247 | }, 1248 | Merge{ from_1, from_2, to } => { 1249 | fmt.write_fmt(format_args!("{from_1}{from_2}^{to}")) 1250 | } 1251 | Promote{ from, to, kind } => { 1252 | let sym = Piece{ kind, is_white: true }.symbol(); 1253 | fmt.write_fmt(format_args!("{from}{to}{sym}")) 1254 | } 1255 | } 1256 | } 1257 | } 1258 | impl fmt::Debug for Move { 1259 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 1260 | fmt::Display::fmt(self, fmt) 1261 | } 1262 | } 1263 | 1264 | pub fn get_probs(state: &ChessState) -> Vec { 1265 | // fn get_prob_matrix_monomorphized(state: &QuantumState) -> aljabar::Matrix 1266 | // where [u8; (N + 7) / 8]: { 1267 | // use aljabar::Zero; 1268 | // let mut out = aljabar::Matrix::zero(); 1269 | // for (bv, val) in state.iter() { 1270 | // let prob = val.norm_squared(); 1271 | // for i in 0.. 64 { 1272 | // if bv[i] { 1273 | // let sq = Square::from_idx(i).unwrap(); 1274 | // out[sq.col.get_idx()][sq.row.get_idx()] += prob as f32; 1275 | // } 1276 | // } 1277 | // } 1278 | // out 1279 | // } 1280 | 1281 | // apply_monomorphized_fn!(&state.occupancy.state, get_prob_matrix_monomorphized, ) 1282 | get_probs_given(state, &[]) 1283 | } 1284 | 1285 | pub fn get_probs_given(state: &ChessState, all_true: &[usize]) -> Vec { 1286 | fn get_prob_matrix_monomorphized(state: &QuantumState, all_true: &[usize]) -> Vec 1287 | where [u8; (N + 7) / 8]: { 1288 | let all_true_bv = all_true.into(); 1289 | let mut out = vec![0.0; N]; 1290 | for (bv, val) in state.iter().filter(|&(bv, _)| (bv & all_true_bv) == all_true_bv) { 1291 | let prob = val.norm_squared(); 1292 | for i in 0.. N { 1293 | if bv[i] { 1294 | out[i] += prob as f32; 1295 | } 1296 | } 1297 | } 1298 | out 1299 | } 1300 | 1301 | apply_monomorphized_fn!(&state.occupancy.state, get_prob_matrix_monomorphized, all_true) 1302 | } 1303 | 1304 | impl Move { 1305 | pub fn parse(str: &mut &str) -> Result { 1306 | let sq1 = Square::parse(str)?; 1307 | match str.as_bytes().get(0) { 1308 | Some(b'^') => { 1309 | *str = &str[1..]; 1310 | let sq2 = Square::parse(str)?; 1311 | let sq3 = Square::parse(str)?; 1312 | return Ok(Move::Split{ from: sq1, to_1: sq2, to_2: sq3 }); 1313 | } 1314 | Some(b'a'..= b'h') => (), 1315 | Some(_) => return Err("Expected '^' or a square".into()), 1316 | None => return Err("Need two squares in move".into()) 1317 | }; 1318 | 1319 | let sq2 = Square::parse(str)?; 1320 | match str.as_bytes().get(0) { 1321 | None => return Ok(Move::Simple{ from: sq1, to: sq2 }), 1322 | Some(b'^') => { 1323 | *str = &str[1..]; 1324 | let sq3 = Square::parse(str)?; 1325 | return Ok(Move::Merge{ from_1: sq1, from_2: sq2, to: sq3 }); 1326 | } 1327 | // TODO: Other piece promitions 1328 | Some(b'Q') => { 1329 | *str = &str[1..]; 1330 | return Ok(Move::Promote{ from: sq1, to: sq2, kind: PieceKind::Queen }) 1331 | } 1332 | _ => return Err(format!("Unexpected trailing characters: {}", str)) 1333 | } 1334 | } 1335 | 1336 | fn update_castling(self, team: bool, mut castling: [[bool; 2]; 2]) -> [[bool; 2]; 2] { 1337 | let team_idx = if team { 0 } else { 1 }; 1338 | let mut update_from = |sq| match sq { 1339 | Square{ col: Column::E, row} if row.perspective(team) == Row::R1 => 1340 | castling[team_idx] = [false; 2], 1341 | Square{ col: Column::A, row } if row.perspective(team) == Row::R1 => 1342 | castling[team_idx][1] = false, 1343 | Square{ col: Column::H, row } if row.perspective(team) == Row::R1 => 1344 | castling[team_idx][0] = false, 1345 | _ => () 1346 | }; 1347 | match self { 1348 | Move::Simple{from, ..} => update_from(from), 1349 | // Maybe I'm also supposed to update to? That feels unecessary 1350 | Move::Split{from, ..} => update_from(from), 1351 | Move::Merge{from_1, from_2, ..} => { 1352 | update_from(from_1); 1353 | update_from(from_2); 1354 | } 1355 | Move::Promote{..} => (), 1356 | } 1357 | 1358 | castling 1359 | } 1360 | } 1361 | -------------------------------------------------------------------------------- /src/druid.rs: -------------------------------------------------------------------------------- 1 | #![allow(incomplete_features)] 2 | #![feature( 3 | format_args_capture, 4 | const_generics, 5 | const_evaluatable_checked, 6 | )] 7 | 8 | use std::rc::Rc; 9 | use std::str::FromStr; 10 | use std::f64::consts::TAU; 11 | 12 | use druid::{ 13 | *, 14 | widget::*, 15 | piet::kurbo::Circle, 16 | }; 17 | 18 | use im::Vector; 19 | 20 | use qc::*; 21 | 22 | #[derive(Clone, Data, Lens)] 23 | struct ChessGame { 24 | state: Rc, 25 | history: Vector, 26 | selection: Selection, 27 | 28 | // Derivative values that are worth caching 29 | // TODO: Worth putting behind an rc? 30 | #[data(ignore)] 31 | occupancy_prob: Vec, 32 | #[data(ignore)] 33 | occupancy_prob_relative: Option>, 34 | 35 | // More or less constant values that need loading 36 | piece_svgs: Rc<[SvgData; 12]>, 37 | } 38 | 39 | #[derive(Copy, Clone, Data, Lens, Debug, PartialEq)] 40 | struct HistItem { 41 | #[data(same_fn = "PartialEq::eq")] 42 | chess_move: Move, 43 | measure: Option, 44 | } 45 | 46 | impl HistItem { 47 | fn to_string(self) -> String { 48 | match self.measure { 49 | Some(true) => format!("{:.5}.m1", self.chess_move), 50 | Some(false) => format!("{:.5}.m0", self.chess_move), 51 | None => format!("{:.5} ", self.chess_move), 52 | } 53 | } 54 | } 55 | 56 | fn main() { 57 | let seed = u64::from_str(&std::env::args().nth(1).unwrap()).unwrap(); 58 | unsafe { 59 | extern "C" { fn srand48(x: u64); } 60 | srand48(seed); 61 | } 62 | 63 | let svg = |src| SvgData::from_str(src).unwrap(); 64 | let piece_svgs = Rc::new([ 65 | svg(include_str!("Chess_plt45.svg")), 66 | svg(include_str!("Chess_nlt45.svg")), 67 | svg(include_str!("Chess_blt45.svg")), 68 | svg(include_str!("Chess_rlt45.svg")), 69 | svg(include_str!("Chess_klt45.svg")), 70 | svg(include_str!("Chess_qlt45.svg")), 71 | svg(include_str!("Chess_pdt45.svg")), 72 | svg(include_str!("Chess_ndt45.svg")), 73 | svg(include_str!("Chess_bdt45.svg")), 74 | svg(include_str!("Chess_rdt45.svg")), 75 | svg(include_str!("Chess_kdt45.svg")), 76 | svg(include_str!("Chess_qdt45.svg")), 77 | ]); 78 | 79 | let main_window = WindowDesc::new(move || build_chess_widget()) 80 | // Convenient for building with cargo watch -x run to make it spawn 81 | // in an out of the way location :) 82 | .window_size((1000.0, 550.0)) 83 | .set_position((2000.0, 1400.0)) 84 | ; 85 | 86 | let state = ChessState::new_game(); 87 | let occupancy_prob = get_probs(&state); 88 | 89 | 90 | let game = ChessGame { 91 | state: Rc::new(state), 92 | history: Vector::new(), 93 | selection: Selection::None, 94 | 95 | occupancy_prob, 96 | occupancy_prob_relative: None, 97 | 98 | piece_svgs, 99 | }; 100 | 101 | AppLauncher::with_window(main_window) 102 | .launch(game) 103 | .expect("Failed to launch"); 104 | } 105 | 106 | fn build_chess_widget() -> impl Widget { 107 | // Start board row 108 | 109 | let board = QChessBoard::new().center() 110 | .padding(SQUARE_SIZE / 2.0); 111 | 112 | fn build_hist_item() -> impl Widget<(usize, HistItem, Option)> { 113 | let label = Label::new(|&(i, _, _): &(usize, HistItem, Option), _: &Env| 114 | format!("{i:.2}.")); 115 | let white_move = Label::new(|&(_, m, _): &(usize, HistItem, Option), _: &Env| 116 | m.to_string()); 117 | let black_move = Label::new(|&(_, _, m): &(usize, HistItem, Option), _: &Env| 118 | if let Some(m) = m { 119 | m.to_string() 120 | } else { format!("") } 121 | ); 122 | 123 | Flex::row() 124 | .with_child(label.fix_width(30.0)) 125 | .with_child(white_move.fix_width(80.0)) 126 | .with_child(black_move.fix_width(80.0)) 127 | } 128 | 129 | fn map_hist(history: &Vector) -> Vector<(usize, HistItem, Option)> { 130 | (0.. (history.len() + 1)/2) 131 | .into_iter() 132 | .map(|i| (i, history[i * 2], history.get(i * 2 + 1).map(|&x| x))) 133 | .collect() 134 | } 135 | let move_hist = List::new(build_hist_item) 136 | .lens(lens::Map::new( 137 | map_hist, 138 | |data, new| debug_assert_eq!(map_hist(&*data), new, "Move numbers is immutable") 139 | )) 140 | .lens(ChessGame::history); 141 | 142 | let move_hist_scroll = Scroll::new(move_hist) 143 | .vertical() 144 | .stick_to_bottom(true) 145 | .fix_size(30.0 + 80.0 + 80.0, SQUARE_SIZE * 6.0) 146 | .padding(SQUARE_SIZE / 2.0); 147 | 148 | // TODO: Would be nice to give this a min-size of 1 piece... 149 | let captured_pieces = Flex::column() 150 | .with_child(QCaptured{white_pieces: true}) 151 | .with_child(QCaptured{white_pieces: false}); 152 | 153 | let board_row = Flex::row() 154 | .with_child(board) 155 | .with_child(captured_pieces) 156 | .with_child(move_hist_scroll); 157 | 158 | // Start state list 159 | 160 | // TODO: Create and use an "infinite scroll list" 161 | fn map_qchess_state(game: &ChessGame) -> Vector { 162 | fn basis_vec(state: &QuantumState, critera: Option, limit: usize) -> Vec<(BasisVector<64>, Complex)> 163 | where [u8; (N+7)/8]: { 164 | state.iter() 165 | .filter(|(bv, _)| critera.map(|c| bv[c]).unwrap_or(true)) 166 | .take(limit) 167 | .map(|(bv, val)| (bv.resize(), val)) 168 | .collect() 169 | } 170 | 171 | let critera = game.selection.get_filter_qbit(); 172 | 173 | let bvs = apply_monomorphized_fn!(&game.state.occupancy.state, basis_vec, critera, 256); 174 | bvs.into_iter().map(|(bv, val)| QChessStateData { 175 | svgs: game.piece_svgs.clone(), 176 | pieces: game.state.pieces, 177 | occupancy: bv, 178 | val 179 | }).collect() 180 | } 181 | 182 | let state_list_list = List::new(|| QChessStateBoard {}) 183 | .horizontal() 184 | .with_spacing(25.0) 185 | .lens(lens::Map::new( 186 | map_qchess_state, 187 | |_data, _new| (), 188 | )); 189 | 190 | let state_list = Scroll::new(state_list_list) 191 | .horizontal() 192 | .padding(50.0); 193 | 194 | Flex::column() 195 | .with_child(board_row.align_left()) 196 | .with_child(state_list) 197 | } 198 | 199 | #[derive(Copy, Clone, Debug, PartialEq)] 200 | enum Selection { 201 | None, 202 | Start(Square), 203 | StartMerge(Square), 204 | MiddleMerge(Square, Square), 205 | StartSplit(Square, Square), 206 | } 207 | 208 | impl Data for Selection { 209 | fn same(&self, other: &Self) -> bool { 210 | self == other 211 | } 212 | } 213 | 214 | impl Selection { 215 | fn left_click(&mut self, state: &ChessState, sq: Square) -> Option { 216 | use Selection::*; 217 | match *self { 218 | None => { 219 | *self = Start(sq); 220 | Option::None 221 | }, 222 | Start(from) => { 223 | *self = None; 224 | // TODO: Add a new selection state for promotion with a selection ui... 225 | if sq.row.perspective(state.is_white_turn) == Row::R8 226 | && state.pieces[from.get_idx()].map(|p| p.kind == PieceKind::Pawn).unwrap_or(false) { 227 | Some(Move::Promote{from, to: sq, kind: PieceKind::Queen}) 228 | } else { 229 | Some(Move::Simple{from, to: sq}) 230 | } 231 | }, 232 | StartMerge(from_1) => { 233 | *self = MiddleMerge(from_1, sq); 234 | Option::None 235 | }, 236 | MiddleMerge(from_1, from_2) => { 237 | *self = None; 238 | Some(Move::Merge{from_1, from_2, to: sq}) 239 | }, 240 | StartSplit(from, to_1) => { 241 | *self = None; 242 | Some(Move::Split{from, to_1, to_2: sq}) 243 | } 244 | } 245 | } 246 | 247 | fn right_click(&mut self, sq: Square) { 248 | use Selection::*; 249 | match *self { 250 | None => { 251 | *self = StartMerge(sq) 252 | }, 253 | Start(from) => { 254 | *self = StartSplit(from, sq) 255 | } 256 | StartMerge(..) | MiddleMerge(..) | StartSplit(..) => { 257 | *self = None 258 | } 259 | } 260 | } 261 | } 262 | 263 | impl Selection { 264 | fn get_filter_qbit(self) -> Option { 265 | match self { 266 | Selection::Start(from) | Selection::StartMerge(from) => { 267 | Some(from.get_idx()) 268 | }, 269 | _ => None 270 | } 271 | } 272 | } 273 | 274 | impl ChessGame { 275 | fn set_occupancy_prob_rel(&mut self) { 276 | // TODO: Other ways to select squares? 277 | // Combining with merge move is weird... 278 | match self.selection.get_filter_qbit() { 279 | Some(from) 280 | => self.occupancy_prob_relative = { 281 | let prob = self.occupancy_prob[from]; 282 | if prob > 0.999 { None } 283 | else { 284 | let mut probs = get_probs_given(&self.state, &[from]); 285 | probs.iter_mut().for_each(|p| *p /= prob); 286 | Some(probs) 287 | } 288 | }, 289 | None => self.occupancy_prob_relative = None, 290 | }; 291 | } 292 | 293 | fn svg(&self, piece: Piece) -> &SvgData { 294 | &self.piece_svgs[svg_idx(piece)] 295 | } 296 | } 297 | 298 | fn svg_idx(piece: Piece) -> usize { 299 | let idx_piece = match piece.kind { 300 | PieceKind::Pawn => 0, 301 | PieceKind::Knight => 1, 302 | PieceKind::Bishop => 2, 303 | PieceKind::Rook => 3, 304 | PieceKind::King => 4, 305 | PieceKind::Queen => 5, 306 | }; 307 | idx_piece + if piece.is_white { 0 } else { 6 } 308 | } 309 | 310 | struct QChessBoard { 311 | } 312 | 313 | impl QChessBoard { 314 | fn new() -> Self { 315 | QChessBoard {} 316 | } 317 | } 318 | 319 | const SQUARE_SIZE: f64 = 80.0; 320 | const CHESS_BOARD_SIZE: Size = Size::new(SQUARE_SIZE * 8.0, SQUARE_SIZE * 8.0); 321 | 322 | const WHITE_SQUARE_COLOR: Color = Color::rgb8(200, 200, 255); 323 | const BLACK_SQUARE_COLOR: Color = Color::rgb8(40, 120, 40); 324 | 325 | fn origin(mut i: usize, j: usize) -> Point { 326 | i = 7 - i; 327 | Point::new(j as f64 * SQUARE_SIZE, i as f64 * SQUARE_SIZE) 328 | } 329 | 330 | fn sq_rect(i: usize, j: usize) -> Rect { 331 | let sq_size = Size::new(SQUARE_SIZE, SQUARE_SIZE); 332 | Rect::from_origin_size(origin(i, j), sq_size) 333 | } 334 | 335 | fn draw_board(ctx: &mut PaintCtx) { 336 | // Draw board 337 | ctx.fill( 338 | Rect::from_origin_size(Point::ZERO, CHESS_BOARD_SIZE), 339 | &WHITE_SQUARE_COLOR, 340 | ); 341 | for i in 0.. 8 { 342 | for j in 0.. 8 { 343 | if (i + j) % 2 == 1 { continue }; 344 | ctx.fill( 345 | sq_rect(i, j), 346 | &BLACK_SQUARE_COLOR 347 | ); 348 | } 349 | } 350 | } 351 | 352 | fn draw_piece(svg: &SvgData, ctx: &mut PaintCtx, origin: Point, scale: f64) { 353 | // svg 354 | let sq_size = Size::new(SQUARE_SIZE, SQUARE_SIZE); 355 | let sq = Rect::from_origin_size(origin, sq_size); 356 | let transform = piet::kurbo::TranslateScale::new( 357 | sq.origin().to_vec2() 358 | + Vec2::new(SQUARE_SIZE, SQUARE_SIZE) * ((1.0 - scale) / 2.0), 359 | (SQUARE_SIZE / 45.0 /* scale to square size */) * scale, 360 | ).into(); 361 | svg.to_piet(transform, ctx); 362 | } 363 | 364 | fn draw_qpiece(game: &ChessGame, ctx: &mut PaintCtx, origin: Point, piece: Piece, qbit: usize) { 365 | draw_piece(game.svg(piece), ctx, origin, 0.75); 366 | 367 | // Probability circles 368 | let prob = game.occupancy_prob[qbit] as f64; 369 | if prob > 0.999 { return }; 370 | let center = origin + (SQUARE_SIZE / 2.0, SQUARE_SIZE / 2.0); 371 | let angle = prob * TAU; 372 | 373 | let prob_rel = 374 | game.occupancy_prob_relative.as_ref().map(|m| m[qbit] as f64); 375 | if prob_rel.map(|p| (p - prob).abs() >= 0.0001).unwrap_or(false) { 376 | let prob_rel = prob_rel.unwrap(); 377 | let angle_rel = prob_rel * TAU; 378 | 379 | prob_circle(ctx, center, SQUARE_SIZE / 2.0 - 3.0, angle, 127); 380 | prob_circle(ctx, center, SQUARE_SIZE / 2.0 - 9.0, angle_rel, 255); 381 | } 382 | else { 383 | prob_circle(ctx, center, SQUARE_SIZE / 2.0 - 3.0, angle, 255); 384 | } 385 | } 386 | 387 | fn prob_circle(ctx: &mut PaintCtx, center: Point, radius: f64, angle: f64, alpha: u8) { 388 | let width_prob = 2.0; 389 | let width_track = 4.0; 390 | 391 | let track = piet::kurbo::Circle::new(center, radius); 392 | ctx.stroke(track, &Color::rgba8(30, 30, 30, alpha), width_track); 393 | 394 | let arc = piet::kurbo::CircleSegment { 395 | center: center, 396 | outer_radius: radius + width_prob / 2.0, 397 | inner_radius: radius - width_prob / 2.0, 398 | start_angle: TAU / 4.0 - angle / 2.0, 399 | sweep_angle: angle, 400 | }; 401 | 402 | ctx.fill( 403 | arc, 404 | &Color::rgba8(255, 40, 255, alpha), 405 | // 5.0, 406 | ) 407 | } 408 | 409 | #[allow(unused_variables)] 410 | impl Widget for QChessBoard { 411 | fn event(&mut self, ctx: &mut EventCtx, event: &Event, game: &mut ChessGame, env: &Env) { 412 | use Event::*; 413 | 414 | let get_square = |point: Point| { 415 | let col_idx = (point.x / SQUARE_SIZE).floor() as usize; 416 | let mut row_idx = (point.y / SQUARE_SIZE).floor() as usize; 417 | row_idx = 7 - row_idx; 418 | let col = Column::from_idx(col_idx); 419 | let row = Row::from_idx(row_idx); 420 | col.and_then(|col| row.map(|row| Square{col, row})) 421 | }; 422 | 423 | match event { 424 | MouseDown(mouse_e) if mouse_e.button.is_left() => { 425 | let sq = get_square(mouse_e.pos); 426 | if let Some(sq) = get_square(mouse_e.pos) { 427 | if let Some(chess_move) = game.selection.left_click(&*game.state, sq) { 428 | if let Ok((new_state, measure)) = game.state.apply_move(chess_move) { 429 | // Could use make_mut instead if somtimes this ptr 430 | // isn't shared... but I think it always is. 431 | new_state.occupancy.print_state(); 432 | game.state = Rc::new(new_state); 433 | game.history.push_back(HistItem{ chess_move, measure }); 434 | game.occupancy_prob = get_probs(&game.state); 435 | } 436 | else { 437 | println!("Illegal move?") 438 | } 439 | } 440 | game.set_occupancy_prob_rel(); 441 | ctx.request_paint(); 442 | } 443 | } 444 | MouseDown(mouse_e) if mouse_e.button.is_right() => { 445 | if let Some(sq) = get_square(mouse_e.pos) { 446 | game.selection.right_click(sq); 447 | game.set_occupancy_prob_rel(); 448 | ctx.request_paint(); 449 | } 450 | } 451 | _ => () 452 | }; 453 | } 454 | 455 | fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, game: &ChessGame, env: &Env) { 456 | } 457 | 458 | fn update(&mut self, ctx: &mut UpdateCtx, old_game: &ChessGame, game: &ChessGame, env: &Env) { 459 | } 460 | 461 | fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, game: &ChessGame, env: &Env) -> Size { 462 | CHESS_BOARD_SIZE 463 | } 464 | 465 | fn paint(&mut self, ctx: &mut PaintCtx, game: &ChessGame, env: &Env) { 466 | draw_board(ctx); 467 | 468 | // Draw pieces 469 | for i in 0.. 8 { 470 | for j in 0.. 8 { 471 | let chess_sq = Square { 472 | row: Row::from_idx(i).unwrap(), 473 | col: Column::from_idx(j).unwrap(), 474 | }; 475 | let piece = game.state.pieces[chess_sq.get_idx()]; 476 | if let Some(piece) = piece { 477 | draw_qpiece(game, ctx, origin(i, j), piece, i * 8 + j); 478 | } 479 | } 480 | } 481 | 482 | // Draw selection 483 | let clip_corner_rad = ((SQUARE_SIZE * SQUARE_SIZE) * (1.0/4.0 + 1.0/16.0)).sqrt(); 484 | let sel_color_1 = Color::rgb8(0, 50, 90); 485 | let sel_color_2 = Color::rgb8(100, 30, 0); 486 | let sel_width = 4.0; 487 | 488 | let draw_sel = |ctx: &mut PaintCtx, sq: Square, col: &Color| { 489 | let rect = sq_rect(sq.row.get_idx(), sq.col.get_idx()); 490 | ctx.stroke(rect, col, sel_width); 491 | }; 492 | 493 | let draw_partial_sel = |ctx: &mut PaintCtx, sq: Square, col: &Color| { 494 | let rect = sq_rect(sq.row.get_idx(), sq.col.get_idx()); 495 | let center = origin(sq.row.get_idx(), sq.col.get_idx()) + (SQUARE_SIZE / 2.0, SQUARE_SIZE / 2.0); 496 | ctx.with_save(|ctx| { 497 | // I'd prefer to stroke the *outisde* of the circle, but it's not obvious how to 498 | // do that with the api so this will suffice for now. 499 | ctx.clip(Circle::new(center, clip_corner_rad)); 500 | ctx.stroke(rect, col, sel_width); 501 | }) 502 | }; 503 | 504 | match game.selection { 505 | Selection::None => (), 506 | Selection::Start(sq) => draw_sel(ctx, sq, &sel_color_1), 507 | Selection::StartMerge(sq) => draw_partial_sel(ctx, sq, &sel_color_1), 508 | Selection::MiddleMerge(sq1, sq2) => { 509 | draw_partial_sel(ctx, sq1, &sel_color_1); 510 | draw_sel(ctx, sq2, &sel_color_1); 511 | } 512 | Selection::StartSplit(from, to) => { 513 | draw_sel(ctx, from, &sel_color_1); 514 | draw_partial_sel(ctx, to, &sel_color_2); 515 | } 516 | } 517 | } 518 | } 519 | 520 | use qc::apply_monomorphized_fn; 521 | use qcomp::{BasisVector, Complex, QuantumState}; 522 | 523 | #[derive(Data, Clone)] 524 | struct QChessStateData { 525 | #[data(ignore)] 526 | svgs: Rc<[SvgData; 12]>, 527 | #[data(same_fn = "PartialEq::eq")] 528 | pieces: [Option; 64], 529 | #[data(same_fn = "PartialEq::eq")] 530 | occupancy: BasisVector<64>, 531 | #[data(same_fn = "PartialEq::eq")] 532 | val: Complex, 533 | } 534 | 535 | /// Widget representing a single basis vector 536 | struct QChessStateBoard {} 537 | #[allow(unused_variables)] 538 | impl Widget for QChessStateBoard { 539 | fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut QChessStateData, env: &Env) { 540 | } 541 | fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &QChessStateData, env: &Env) { 542 | } 543 | fn update(&mut self, ctx: &mut UpdateCtx, old_data: &QChessStateData, data: &QChessStateData, env: &Env) { 544 | } 545 | fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &QChessStateData, env: &Env) -> Size { 546 | CHESS_BOARD_SIZE * 0.25 547 | } 548 | fn paint( 549 | &mut self, 550 | ctx: &mut PaintCtx, 551 | data: &QChessStateData, 552 | _env: &Env 553 | ) { 554 | ctx.transform(Affine::scale(0.25)); 555 | 556 | draw_board(ctx); 557 | 558 | for i in 0.. 8 { 559 | for j in 0.. 8 { 560 | if !data.occupancy[i * 8 + j] { continue }; 561 | let chess_sq = Square { 562 | row: Row::from_idx(i).unwrap(), 563 | col: Column::from_idx(j).unwrap(), 564 | }; 565 | let piece = data.pieces[chess_sq.get_idx()]; 566 | if let Some(piece) = piece { 567 | let svg = &data.svgs[svg_idx(piece)]; 568 | draw_piece(svg, ctx, origin(i, j), 0.9); 569 | } 570 | } 571 | } 572 | 573 | 574 | let half_board = SQUARE_SIZE * 4.0; 575 | let center = Point::new(half_board, half_board); 576 | let circle = Circle::new(center, half_board); 577 | ctx.stroke(circle, &Color::rgba8(50, 0, 0, 128), 4.0); 578 | 579 | let x = data.val[0] * half_board + half_board; 580 | let y = data.val[1] * half_board + half_board; 581 | let end_point = Point::new(x, y); 582 | let line = piet::kurbo::Line::new(center, end_point); 583 | ctx.stroke(line, &Color::rgba8(200, 0, 0, 128), 16.0); 584 | } 585 | } 586 | 587 | impl ChessGame { 588 | fn captured_pieces<'a>(&'a self, white_pieces: bool) -> impl Iterator + 'a { 589 | (64.. self.occupancy_prob.len()).into_iter().filter_map(move |qbit| { 590 | // We set piece for every capture qubits, also only capture qubits should still 591 | // matter (but I need to work on the APIs exposing all of this) 592 | if let Some(piece) = self.state.occupancy.qbits[qbit].piece { 593 | if piece.is_white != white_pieces { return None }; 594 | 595 | let prob = self.occupancy_prob[qbit] as f64; 596 | if prob == 0.0 || prob > 0.999 { return None }; 597 | 598 | Some((piece, qbit)) 599 | } 600 | else { 601 | None 602 | } 603 | }) 604 | } 605 | } 606 | 607 | struct QCaptured { 608 | white_pieces: bool, 609 | } 610 | impl Widget for QCaptured { 611 | fn event(&mut self, _ctx: &mut EventCtx, _event: &Event, _data: &mut ChessGame, _env: &Env) { 612 | // No input handling for now, maybe some sort of analysis selection 613 | // eventually, and inspecting when pieces were captured 614 | } 615 | fn lifecycle(&mut self, _ctx: &mut LifeCycleCtx, _event: &LifeCycle, _data: &ChessGame, _env: &Env) { 616 | } 617 | fn update(&mut self, _ctx: &mut UpdateCtx, _old_data: &ChessGame, _data: &ChessGame, _env: &Env) { 618 | } 619 | fn layout(&mut self, _ctx: &mut LayoutCtx, _bc: &BoxConstraints, game: &ChessGame, _env: &Env) -> Size { 620 | let width = ((game.captured_pieces(self.white_pieces).count() + 7) / 8) as f64 * SQUARE_SIZE; 621 | let height = 8.0 * SQUARE_SIZE; 622 | // Apply scale... 623 | Size::new(width, height) * 0.5 624 | } 625 | fn paint(&mut self, ctx: &mut PaintCtx, game: &ChessGame, _: &Env) { 626 | // TODO: Do I need to restore here? 627 | // Draw captured pieces at half size 628 | ctx.transform(Affine::scale(0.5)); 629 | 630 | for (idx, (piece, qbit)) in game.captured_pieces(self.white_pieces).enumerate() { 631 | let mut i = idx % 8; 632 | if !self.white_pieces { i = 7 - i }; 633 | let j = idx / 8; 634 | let origin = Point::new(j as f64 * SQUARE_SIZE, i as f64 * SQUARE_SIZE); 635 | draw_qpiece(game, ctx, origin, piece, qbit); 636 | } 637 | } 638 | } 639 | -------------------------------------------------------------------------------- /src/gates.rs: -------------------------------------------------------------------------------- 1 | 2 | use crate::qcomp::Matrix; 3 | use aljabar::*; 4 | 5 | fn tensor_product(mat1: Matrix, mat2: Matrix) -> Matrix<{N * M}> { 6 | let mut out = Matrix::zero(); 7 | for m_j in 0.. M { 8 | for m_i in 0.. M { 9 | for n_j in 0.. N { 10 | for n_i in 0.. N { 11 | out[m_j * N + n_j][m_i * N + n_i] = mat1[m_j][m_i] * mat2[n_j][n_i]; 12 | } 13 | } 14 | } 15 | } 16 | out 17 | } 18 | 19 | /// Logical not of a single qubit. 20 | pub fn pauli_x() -> Matrix<2> { 21 | matrix_c![ 22 | [ 0.0, 1.0 ], 23 | [ 1.0, 0.0 ] 24 | ] 25 | } 26 | 27 | pub fn swap() -> Matrix<4> { 28 | matrix_c![ 29 | [ 1.0, 0.0, 0.0, 0.0 ], 30 | [ 0.0, 0.0, 1.0, 0.0 ], 31 | [ 0.0, 1.0, 0.0, 0.0 ], 32 | [ 0.0, 0.0, 0.0, 1.0 ] 33 | ] 34 | } 35 | 36 | fn swap3_12() -> Matrix<8> { 37 | tensor_product(Matrix::<2>::one(), swap()) 38 | } 39 | 40 | pub fn iswap() -> Matrix<4> { 41 | matrix_c![ 42 | [ 1.0, 0.0, 0.0, 0.0 ], 43 | [ 0.0, 0.0, (i 1.0), 0.0 ], 44 | [ 0.0, (i 1.0), 0.0, 0.0 ], 45 | [ 0.0, 0.0, 0.0, 1.0 ] 46 | ] 47 | } 48 | 49 | pub fn iswap3_01() -> Matrix<8> { 50 | tensor_product(Matrix::<2>::one(), iswap()) 51 | } 52 | 53 | pub fn iswap3_02() -> Matrix<8> { 54 | swap3_12() * iswap3_01() * swap3_12() 55 | } 56 | 57 | /// Swap qbit 0 to qbit 1, then qbit 1 to qbit 2 58 | pub fn iswap3_01_12() -> Matrix<8> { 59 | let ident: Matrix<2> = Matrix::one(); 60 | tensor_product(iswap(), ident) * tensor_product(ident, iswap()) 61 | } 62 | 63 | pub fn iswap4_01_23() -> Matrix<16> { 64 | let ident: Matrix<4> = Matrix::one(); 65 | tensor_product(iswap(), ident) * tensor_product(ident, iswap()) 66 | } 67 | 68 | pub fn iswap5_43_21_10() -> Matrix<32> { 69 | tensor_product(Matrix::<8>::one(), iswap()) // 1, 0 70 | * tensor_product(Matrix::<4>::one(), tensor_product(iswap(), Matrix::<2>::one())) // 2, 1 71 | * tensor_product(iswap(), Matrix::<8>::one()) // 4, 3 72 | } 73 | 74 | /// from, to_1, to_2, to_1 from low to high qubit 75 | pub fn qc_split() -> Matrix<8> { 76 | use std::f64::consts::FRAC_1_SQRT_2; 77 | matrix_c![ 78 | [ 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ], 79 | [ 0.0, 0.0, 0.0, 0.0, (i 1.0), 0.0, 0.0, 0.0 ], 80 | [ 0.0, (i FRAC_1_SQRT_2), FRAC_1_SQRT_2, 0.0, 0.0, 0.0, 0.0, 0.0 ], 81 | [ 0.0, 0.0, 0.0, 0.0, 0.0, (-FRAC_1_SQRT_2), (i FRAC_1_SQRT_2), 0.0 ], 82 | [ 0.0, (i FRAC_1_SQRT_2), (-FRAC_1_SQRT_2), 0.0, 0.0, 0.0, 0.0, 0.0 ], 83 | [ 0.0, 0.0, 0.0, 0.0, 0.0, (i FRAC_1_SQRT_2), (-FRAC_1_SQRT_2), 0.0 ], 84 | [ 0.0, 0.0, 0.0, (i 1.0), 0.0, 0.0, 0.0, 0.0 ], 85 | [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] 86 | ] 87 | } 88 | 89 | /// to, from_2, from_1 from low to high qubit 90 | pub fn qc_merge() -> Matrix<8> { 91 | use std::f64::consts::FRAC_1_SQRT_2; 92 | matrix_c![ 93 | [ 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ], 94 | [ 0.0, 0.0, (i -FRAC_1_SQRT_2), 0.0, (i -FRAC_1_SQRT_2), 0.0, 0.0, 0.0 ], 95 | [ 0.0, 0.0, FRAC_1_SQRT_2, 0.0, (-FRAC_1_SQRT_2), 0.0, 0.0, 0.0 ], 96 | [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, (i -1.0), 0.0 ], 97 | [ 0.0, (i -1.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ], 98 | [ 0.0, 0.0, 0.0, (-FRAC_1_SQRT_2), 0.0, (i -FRAC_1_SQRT_2), 0.0, 0.0 ], 99 | [ 0.0, 0.0, 0.0, (i -FRAC_1_SQRT_2), 0.0, (-FRAC_1_SQRT_2), 0.0, 0.0 ], 100 | [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] 101 | ] 102 | } 103 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /src/qcomp.rs: -------------------------------------------------------------------------------- 1 | /// Implementation of a quantum computer simulator 2 | /// 3 | /// We favor brute force speed over smart algorithms, 4 | /// which makes sense for some problems and not others. 5 | /// 6 | /// Only supports quantum states of a fixed set of number 7 | /// of qbits. That set can be modified by modifying the 8 | /// list of numbers in `with_bound_const`. This allows us 9 | /// to get away with using *very* few allocations and while 10 | /// I haven't benchmarked I believe means we should be very 11 | /// fast on reasonably complex states. 12 | /// 13 | /// The storage is optimized for pure states with relatively 14 | /// few non-zero computational basis elements. If most 15 | /// computational basis elements are non zero this could be 16 | /// made much faster by replacing 17 | /// `decomposed: HashMap, Complex>` 18 | /// with `decomposed: [Complex; 1 << N]`. It should be easy 19 | /// to put this variation behind a feature flag if anyone is 20 | /// interested in it. 21 | /// 22 | /// Has support for allocating and freeing qbits, and soon 23 | /// to have support for reversing computation to previous 24 | /// states. Both of these could be easily removed or moved 25 | /// behind a feature flag for minor performance improvements. 26 | /// Support for freeing qbits is currently sub-optimal (they 27 | /// are only freed if they reach a |0> or |1> state, when they 28 | /// could be freed as soon as they are in a set of freed qbits 29 | /// that is tensor producted with the non-freed state). 30 | 31 | use std::{ 32 | collections::HashMap, 33 | fmt, 34 | marker::PhantomData, 35 | ops::{Index, Rem, Sub, Add, BitAnd, BitAndAssign, BitOr, BitOrAssign, Not}, 36 | }; 37 | 38 | use aljabar::*; 39 | 40 | use crate::c; 41 | 42 | 43 | 44 | // Can't use with_const in apply_monomorphized_fn 45 | // because assigning N to a const makes the compiler 46 | // forget the bounds on N :'( 47 | #[macro_export] 48 | macro_rules! with_bound_const { 49 | ($name:ident with $ty:ident from $type_builder:tt= $val:expr; $($cval:literal)* $expr:block) => { 50 | match $val { 51 | $( 52 | $cval => { 53 | type $ty = $type_builder <$cval>; 54 | #[allow(dead_code)] 55 | const $name: usize = $cval; 56 | $expr 57 | } 58 | ),* 59 | _ => panic!("Unexpected value in with_const") 60 | } 61 | }; 62 | 63 | ($name:ident with $ty:ident from $type_builder:tt= $val:expr; NQBITS $expr:block) => { 64 | crate::with_bound_const!($name with $ty from $type_builder = $val; 65 | 64 65 66 67 68 69 66 | 70 71 72 73 74 75 76 77 78 79 67 | 80 81 82 83 84 85 86 87 88 89 68 | 90 91 92 93 94 95 96 97 98 99 69 | 100 101 102 103 104 105 106 107 108 109 70 | 110 111 112 113 114 115 116 117 118 119 71 | 120 121 122 123 124 125 126 127 128 129 72 | 130 131 132 133 134 135 136 73 | $expr 74 | ) 75 | } 76 | } 77 | 78 | #[macro_export] 79 | macro_rules! apply_monomorphized_fn { 80 | ($state:expr, $func: expr, $($arg:expr),*) => {{ 81 | let state = $state; 82 | let n = state.state_size(); 83 | let any = state.as_any(); 84 | 85 | crate::with_bound_const!(N with QuantumStateN from QuantumState = n; 86 | NQBITS 87 | { 88 | let state = any.downcast_ref::().unwrap(); 89 | $func(state, $($arg),*) 90 | }) 91 | }} 92 | } 93 | 94 | // Defining some useful types 95 | 96 | // Rational number type 97 | // TODO: Make this confirurable with an arbitrary precision option? 98 | // I think I just need rat + 1/nat power. 99 | pub type Real = f64; 100 | pub type Matrix = aljabar::Matrix; 101 | #[derive(Copy, Clone, PartialEq)] 102 | pub struct Complex(pub [Real; 2]); 103 | 104 | mod complex_impl { 105 | // Adding unecessary modules to allow for code folding? 106 | // Doesn't sound like me at all! 107 | use std::ops::{Add, AddAssign, Index, IndexMut, Mul, Sub, Div}; 108 | use std::fmt; 109 | use aljabar::{Zero, One}; 110 | use super::{Complex, Real}; 111 | 112 | impl Complex { 113 | pub fn norm_squared(self) -> Real { 114 | self[0] * self[0] + self[1] * self[1] 115 | } 116 | } 117 | 118 | impl Zero for Complex { 119 | fn zero() -> Self { 120 | Complex([0.0; 2]) 121 | } 122 | 123 | fn is_zero(&self) -> bool { 124 | self == &Self::zero() 125 | } 126 | } 127 | 128 | impl One for Complex { 129 | fn one() -> Self { 130 | Complex([1.0, 0.0]) 131 | } 132 | 133 | fn is_one(&self) -> bool { 134 | self == &Self::one() 135 | } 136 | } 137 | 138 | impl Eq for Complex {} 139 | 140 | impl Index for Complex { 141 | type Output = f64; 142 | fn index(&self, idx: usize) -> &f64 { 143 | &self.0[idx] 144 | } 145 | } 146 | 147 | impl IndexMut for Complex { 148 | fn index_mut(&mut self, idx: usize) -> &mut f64 { 149 | &mut self.0[idx] 150 | } 151 | } 152 | 153 | impl Add for Complex { 154 | type Output = Self; 155 | fn add(self, rhs: Self) -> Self { 156 | let mut out = Self::zero(); 157 | for i in 0.. 2 { 158 | out[i] += self.0[i] + rhs[i]; 159 | } 160 | out 161 | } 162 | } 163 | 164 | impl Mul for Complex { 165 | type Output = Self; 166 | fn mul(self, rhs: Self) -> Self { 167 | Complex([self[0] * rhs[0] - self[1] * rhs[1], self[0] * rhs[1] + self[1] * rhs[0]]) 168 | } 169 | } 170 | 171 | impl Sub for Complex { 172 | type Output = Self; 173 | fn sub(self, rhs: Self) -> Self { 174 | Complex([self[0] - rhs[0], self[1] - rhs[1]]) 175 | } 176 | } 177 | 178 | impl Div for Complex { 179 | type Output = Self; 180 | fn div(self, rhs: Self) -> Self { 181 | if rhs[1] != 0.0 { todo!() } 182 | Complex([self[0] / rhs[0], self[1] / rhs[0]]) 183 | } 184 | } 185 | 186 | impl AddAssign for Complex { 187 | fn add_assign(&mut self, rhs: Self) { 188 | for i in 0.. 2 { 189 | self.0[i] += rhs[i]; 190 | } 191 | } 192 | } 193 | 194 | impl fmt::Debug for Complex { 195 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 196 | match (self[0] == 0.0, self[1] == 0.0) { 197 | (true, true) => fmt.write_str("0"), 198 | (false, true) => fmt.write_fmt(format_args!("{}", self[0])), 199 | (true, false) => fmt.write_fmt(format_args!("(i {})", self[1])), 200 | (false, false) => fmt.write_fmt(format_args!("({} + i {})", self[0], self[1])) 201 | } 202 | } 203 | } 204 | } 205 | 206 | /// Generally an internel implementation detail, used to allow for 207 | /// object-safe trait implementation. 208 | pub struct DynamicMatrix<'a> { 209 | n: usize, 210 | matrix: *const (), 211 | _marker: PhantomData<&'a ()> 212 | } 213 | 214 | impl<'a> DynamicMatrix<'a> { 215 | pub fn new(mat: &'a Matrix) -> Self { 216 | DynamicMatrix { 217 | n: N, 218 | matrix: mat as *const _ as *const (), 219 | _marker: PhantomData, 220 | } 221 | } 222 | 223 | fn into_fixed(&self) -> &Matrix { 224 | unsafe { 225 | assert_eq!(self.n, N); 226 | &*(self.matrix as *const Matrix) 227 | } 228 | } 229 | } 230 | 231 | // a 64 bit bit vector, convenientally just large enough 232 | // for a chess board 233 | #[derive(Copy, Clone, Hash, PartialEq, Eq)] 234 | pub struct BasisVector 235 | where [u8; (BITS + 7) / 8]: { 236 | // Note that the left over in bytes we aren't using 237 | // must be set to zero. 238 | bytes: [u8; (BITS + 7) / 8], 239 | } 240 | impl From for BasisVector 241 | where [u8; (N + 7) / 8]: ,{ 242 | fn from(x: u64) -> Self { 243 | use std::convert::TryFrom; 244 | let x_bytes = x.to_le_bytes(); 245 | let bytes = <[u8; (N + 7) / 8]>::try_from(&x_bytes[0.. (N + 7) / 8]).unwrap(); 246 | Self{ bytes } 247 | } 248 | } 249 | impl<'a, const N: usize> From<&'a [usize]> for BasisVector 250 | where [u8; (N + 7) / 8]: { 251 | fn from(indicies: &'a [usize]) -> Self { 252 | let mut ret = Self::zero(); 253 | for &i in indicies { 254 | ret.set(i, true); 255 | } 256 | ret 257 | } 258 | } 259 | impl Index for BasisVector 260 | where [u8; (N + 7) / 8]: ,{ 261 | type Output=bool; 262 | fn index(&self, idx: usize) -> &bool { 263 | if (self.bytes[idx / 8] >> (idx % 8)) & 0x1 == 1 { 264 | &true 265 | } else { &false } 266 | } 267 | } 268 | impl BasisVector 269 | where [u8; (N + 7) / 8]: ,{ 270 | fn find_one(&self) -> Option { 271 | for (i, byte) in self.bytes.iter().enumerate() { 272 | let tz = byte.trailing_zeros(); 273 | if tz != 8 { 274 | if (i * 8 + tz as usize) > N { 275 | panic!("wtf?"); 276 | } 277 | return Some(i * 8 + tz as usize); 278 | } 279 | } 280 | None 281 | } 282 | 283 | fn leading_ones(&self) -> usize { 284 | if N == 0 { return 0 }; 285 | let initial; 286 | if N % 8 == 0 { 287 | initial = 0; 288 | } 289 | else { 290 | let last_byte = self.bytes.last().unwrap(); 291 | let tail_len = 8 - (N % 8); 292 | initial = (last_byte << tail_len).leading_ones() as usize; 293 | if initial != N % 8 { 294 | return initial; 295 | } 296 | } 297 | 298 | for (i, byte) in self.bytes.iter().rev().skip(1).enumerate() { 299 | let trail = byte.leading_ones(); 300 | if trail != 8 { 301 | return initial + i * 8 + trail as usize; 302 | } 303 | } 304 | 305 | assert_eq!(N % 8, 0); 306 | return N 307 | } 308 | 309 | pub fn set(&mut self, idx: usize, val: bool) { 310 | let byte = &mut self.bytes[idx / 8]; 311 | let bit = idx % 8; 312 | if val { 313 | *byte |= 1 << bit 314 | } else { 315 | *byte &= !(1 << bit) 316 | } 317 | } 318 | 319 | pub fn zero() -> Self { 320 | Self{ bytes: [0; (N + 7) / 8] } 321 | } 322 | 323 | fn max() -> Self { 324 | let mut bv = Self{ bytes: [!0; (N + 7) / 8] }; 325 | // Zero out extra in last byte if needed 326 | if N % 8 != 0 { 327 | let used_bits = N % 8; 328 | bv.bytes[bv.bytes.len() - 1] &= (!0) >> (8 - used_bits); 329 | } 330 | bv 331 | } 332 | 333 | // Keep private to make it harder to confuse indicies and BasisVectors 334 | fn from_usize(x: usize) -> Self { 335 | let mut out = Self::zero(); 336 | let x_bytes = x.to_le_bytes(); 337 | let l = ::std::mem::size_of::().min((N + 7) / 8); 338 | out.bytes[0.. l].copy_from_slice(&x_bytes[0.. l]); 339 | out 340 | } 341 | 342 | /// Adds |0> qubits on the end, or removes qubits from the end 343 | pub fn resize(&self) -> BasisVector 344 | where [u8; (M + 7) / 8]: ,{ 345 | let mut bv = BasisVector::::zero(); 346 | let l_n = (N + 7) / 8; 347 | let l_m = (M + 7) / 8; 348 | for i in 0.. l_n.min(l_m) { 349 | bv.bytes[i] = self.bytes[i] 350 | } 351 | // Zero out extra in last byte if needed 352 | if N > M && (M % 8) != 0 { 353 | let used_bits = M % 8; 354 | bv.bytes[l_m - 1] &= (!0) >> (8 - used_bits); 355 | } 356 | bv 357 | } 358 | } 359 | impl fmt::Debug for BasisVector 360 | where [u8; (N + 7) / 8]: ,{ 361 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 362 | use std::str; 363 | // Would make this const... but it complains that the length might overflow 364 | // and thererfore the type isn't well formed :'(. It's also much 365 | // simpler to just use a dynamic array, and it's just debugging code, so meh. 366 | let mut out = Vec::with_capacity(N + N/8); 367 | for i in 0.. N { 368 | if i != 0 && i % 8 == 0 { 369 | out.push(b'_'); 370 | } 371 | out.push(if self[i] { b'1' } else { b'0' }); 372 | } 373 | fmt.write_fmt(format_args!("bv[{}]{} {:x?}", N, str::from_utf8(&out).unwrap(), self.bytes)) 374 | } 375 | } 376 | impl Rem for BasisVector 377 | where [u8; (N + 7) / 8]: ,{ 378 | type Output = usize; 379 | fn rem(self, rhs: usize) -> usize { 380 | assert!(rhs.is_power_of_two() && rhs <= 256, "Unsupported modulos for bitvec {}", rhs); 381 | self.bytes[0] as usize % rhs 382 | } 383 | } 384 | impl Sub for BasisVector 385 | where [u8; (N + 7) / 8]: ,{ 386 | type Output = Self; 387 | fn sub(mut self, rhs: usize) -> Self { 388 | let rhs = Self::from_usize(rhs); 389 | let mut carry: u8 = 0; 390 | for idx in 0.. (N + 7) / 8 { 391 | if rhs.bytes[idx] == !0 && carry == 1 { 392 | // Subtract 256, i.e. do nothing to this byte and carry remains 1. 393 | continue 394 | } 395 | let sub = rhs.bytes[idx] + carry; 396 | let (r, overflow) = self.bytes[idx].overflowing_sub(sub); 397 | self.bytes[idx] = r; 398 | carry = if overflow { 1 } else { 0 }; 399 | } 400 | 401 | if carry == 1 { 402 | panic!("Subtraction undeflow") 403 | } 404 | 405 | self 406 | } 407 | } 408 | impl Add for BasisVector 409 | where [u8; (N + 7) / 8]: ,{ 410 | type Output = Self; 411 | fn add(mut self, rhs: usize) -> Self { 412 | let rhs = Self::from_usize(rhs); 413 | let mut carry = 0; 414 | for idx in 0.. (N + 7) / 8 { 415 | let r = self.bytes[idx] as u32 + rhs.bytes[idx] as u32 + carry; 416 | self.bytes[idx] = (r % 256) as u8; 417 | carry = r / 256; 418 | } 419 | self 420 | } 421 | } 422 | impl BitAndAssign for BasisVector 423 | where [u8; (N + 7) / 8]: { 424 | fn bitand_assign(&mut self, rhs: Self) { 425 | for idx in 0.. (N + 7) / 8 { 426 | self.bytes[idx] &= rhs.bytes[idx] 427 | } 428 | } 429 | } 430 | impl BitAnd for BasisVector 431 | where [u8; (N + 7) / 8]: { 432 | type Output = Self; 433 | fn bitand(mut self, rhs: Self) -> Self { 434 | self &= rhs; 435 | self 436 | } 437 | } 438 | impl BitOrAssign for BasisVector 439 | where [u8; (N + 7) / 8]: { 440 | fn bitor_assign(&mut self, rhs: Self) { 441 | for idx in 0.. (N + 7) / 8 { 442 | self.bytes[idx] |= rhs.bytes[idx] 443 | } 444 | } 445 | } 446 | impl BitOr for BasisVector 447 | where [u8; (N + 7) / 8]: { 448 | type Output = Self; 449 | fn bitor(mut self, rhs: Self) -> Self { 450 | self |= rhs; 451 | self 452 | } 453 | } 454 | impl Not for BasisVector 455 | where [u8; (N + 7) / 8]: { 456 | type Output = Self; 457 | fn not(mut self) -> Self { 458 | for byte in &mut self.bytes { 459 | *byte = !*byte; 460 | } 461 | // Zero out extra in last byte if needed 462 | if N % 8 != 0 { 463 | let used_bits = N % 8; 464 | self.bytes[self.bytes.len() - 1] &= (!0) >> (8 - used_bits); 465 | } 466 | self 467 | } 468 | } 469 | 470 | /// N QuBit quantum state 471 | #[derive(Clone, Debug)] 472 | pub struct QuantumState 473 | where [u8; (N + 7) / 8]: ,{ 474 | /// Fully expanded, this would be 2^64 complex numbers... 475 | /// Very likely want to consider using im for this! 476 | pub decomposed: HashMap, Complex>, 477 | 478 | /// Qubits which have been freed (but may still be entangled) 479 | pub freed_qubits: BasisVector, 480 | /// Qubits which have been freed and are no longer in use, in a 481 | /// |0> state. 482 | pub reusable_qubits_zeros: BasisVector, 483 | /// Qubits which have been freed and are no longer in use, in a 484 | /// |1> state. 485 | pub reusable_qubits_ones: BasisVector, 486 | } 487 | 488 | impl PartialEq for QuantumState 489 | where [u8; (N + 7) / 8]: { 490 | fn eq(&self, rhs: &QuantumState) -> bool { 491 | self.decomposed == rhs.decomposed 492 | } 493 | } 494 | 495 | 496 | /// Only works on powers of 2 497 | pub(crate) const fn simple_log_2(x: usize) -> usize { 498 | assert!(x.is_power_of_two()); 499 | x.trailing_zeros() as usize 500 | } 501 | 502 | pub trait QuantumSimulator: std::fmt::Debug + std::any::Any { 503 | fn apply_unitary_dynamic( 504 | &self, 505 | u: DynamicMatrix, 506 | indicies: &[usize], 507 | ) -> Box; 508 | 509 | fn apply_controlled_unitaries_dynamic( 510 | &self, 511 | // Matrix, control true, control false. 512 | unitaries: &[( 513 | DynamicMatrix, 514 | Option<&[usize]>, 515 | &[usize], 516 | &[usize], 517 | )], 518 | indicies: &[usize], 519 | ) -> Box; 520 | 521 | fn measure(&mut self, qubit: usize) -> (bool, Real); 522 | 523 | fn state_size(&self) -> usize; 524 | 525 | /// Get a new qubit. If no qubit's have been freed and we have 526 | /// not yet hit our maximum, this returns qubit n where n qubits 527 | /// have been previously allocated (useful if you want non 528 | /// ancilla qubits at fixed positions). After qubits have been 529 | /// freed all bets are off. 530 | fn alloc_qubit(self: Box) -> (Box, usize); 531 | fn alloc_qubit_ref(&self) -> (Box, usize); 532 | /// Tell the implementation that *we* are done with this qubit, 533 | /// the qubit does not need to be in a |0>/product/... state. 534 | fn free_qubit(&mut self, qubit: usize); 535 | // fn run_gc(self: Box) -> (Box); 536 | 537 | #[doc(hidden)] 538 | fn as_any(&self) -> &dyn std::any::Any; 539 | #[doc(hidden)] 540 | fn eq_any(&self, other: &dyn QuantumSimulator) -> bool; 541 | } 542 | 543 | impl PartialEq for dyn QuantumSimulator { 544 | fn eq(&self, other: &Self) -> bool { 545 | self.eq_any(other) 546 | } 547 | } 548 | 549 | impl QuantumSimulator for QuantumState 550 | where [u8; (N + 7) / 8]: { 551 | fn apply_unitary_dynamic( 552 | &self, 553 | u: DynamicMatrix, 554 | indicies: &[usize], 555 | ) -> Box { 556 | use std::convert::TryFrom; 557 | // Too big for the stack at big sizes! 558 | // Unfortunate that this trick means we aren't inlining matrix values anymore... 559 | // but probably impossible. 560 | crate::with_const!(N: usize = u.n; 2 4 8 16 32 64 128 256 1024 { 561 | assert_eq!(indicies.len(), simple_log_2(N)); 562 | let matrix = u.into_fixed::(); 563 | let indicies = <[usize; simple_log_2(N)]>::try_from(indicies).unwrap(); 564 | self.apply_fixed_unitary(matrix, indicies) 565 | }) 566 | } 567 | 568 | fn apply_controlled_unitaries_dynamic( 569 | &self, 570 | unitaries: &[( 571 | DynamicMatrix, 572 | Option<&[usize]>, 573 | &[usize], 574 | &[usize], 575 | )], 576 | indicies: &[usize] 577 | ) -> Box { 578 | use std::convert::TryFrom; 579 | crate::with_const!(LOG2_UN: usize = indicies.len(); 1 2 3 4 5 { 580 | let indicies = <[usize; LOG2_UN]>::try_from(indicies).unwrap(); 581 | 582 | crate::with_const!(UNN: usize = unitaries.len();1 2 3 4 5 { 583 | let mut fixed_us = [ControlledUnitary::::zero(); UNN]; 584 | for i in 0.. UNN { 585 | assert_eq!(unitaries[i].0.n, 1 << LOG2_UN); 586 | fixed_us[i].matrix = *unitaries[i].0.into_fixed::<{1 << LOG2_UN}>(); 587 | if let Some(c_true) = unitaries[i].1 { 588 | fixed_us[i].control_any_true = Some(c_true.into()); 589 | } 590 | fixed_us[i].control_all_true = unitaries[i].2.into(); 591 | fixed_us[i].control_all_false = unitaries[i].3.into(); 592 | } 593 | 594 | self.apply_controlled_unitaries_fixed( 595 | fixed_us, 596 | indicies 597 | ) 598 | }) 599 | }) 600 | } 601 | 602 | fn measure(&mut self, idx: usize) -> (bool, Real) { 603 | let mut prob_true = 0.0; 604 | for (basis, v) in self.decomposed.iter() { 605 | if basis[idx] { 606 | prob_true += v.norm_squared() 607 | } 608 | } 609 | 610 | let measure = prob_true >= rand(); 611 | let prob = if measure { prob_true } else { 1.0 - prob_true }; 612 | self.decomposed.drain_filter(|basis, val| { 613 | // Renormalize 614 | *val = *val / c!(prob.sqrt()); 615 | basis[idx] != measure 616 | }).for_each(drop); 617 | 618 | self.update_reusability(); 619 | (measure, prob) 620 | } 621 | 622 | fn state_size(&self) -> usize { N } 623 | 624 | fn alloc_qubit_ref(&self) -> (Box, usize) { 625 | if let Some(idx) = self.reusable_qubits_zeros.find_one() { 626 | let mut out = self.clone(); 627 | out.reusable_qubits_zeros.set(idx, false); 628 | return (Box::new(out), idx) 629 | } 630 | 631 | if let Some(idx) = self.reusable_qubits_ones.find_one() { 632 | // TODO: alloc_qubit_ref_mut to avoid cloning? 633 | let mut out = self.clone(); 634 | out.reusable_qubits_ones.set(idx, false); 635 | // Set qubit to zero 636 | let new_iter = self.iter().map(|(mut bv, c)| { 637 | bv.set(idx, false); 638 | (bv, c) 639 | }); 640 | out.decomposed = new_iter.collect(); 641 | return (Box::new(out), idx); 642 | } 643 | 644 | 645 | // Roughly *half* my compile time is these 4 lines of code, because it 646 | // generates code sized quadratic in the number of supported qbits. 647 | // I haven't been able to figure out how to avoid this, because I"m not allowed 648 | // to do "N+1" because "that might panic" in types. 649 | crate::with_bound_const!(M with QuantumStateM from QuantumState = N+1; NQBITS { 650 | let out: QuantumStateM = self.resize(); 651 | (Box::new(out), N) 652 | }) 653 | } 654 | 655 | fn alloc_qubit(mut self: Box) -> (Box, usize) { 656 | if let Some(idx) = self.reusable_qubits_zeros.find_one() { 657 | self.reusable_qubits_zeros.set(idx, false); 658 | return (self, idx) 659 | } 660 | 661 | self.alloc_qubit_ref() 662 | } 663 | 664 | fn free_qubit(&mut self, qubit: usize) { 665 | self.freed_qubits.set(qubit, true); 666 | self.update_reusability(); 667 | } 668 | 669 | // fn run_gc(self: Box) -> (Box) { 670 | // todo!() 671 | // } 672 | 673 | fn as_any(&self) -> &dyn std::any::Any { 674 | self 675 | } 676 | fn eq_any(&self, other: &dyn QuantumSimulator) -> bool { 677 | // Quadratically many functions in number of qubits supported... 678 | // probably not ideal... 679 | fn is_eq(lhs: &QuantumState, rhs: &QuantumState) -> bool 680 | where [u8; (M + 7) / 8]: ,[u8; (N + 7) / 8]: { 681 | assert!(N >= M); 682 | 683 | // Check that we don't use state past M 684 | if N != M { 685 | let reusable = lhs.reusable_qubits_ones | lhs.reusable_qubits_zeros; 686 | if N - reusable.leading_ones() > M { 687 | return false 688 | } 689 | } 690 | 691 | for (bv, val) in lhs.iter() { 692 | if rhs.decomposed.get(&bv.resize()) != Some(&val) { 693 | return false; 694 | } 695 | } 696 | 697 | true 698 | } 699 | 700 | fn is_eq_unsorted(lhs: &QuantumState, rhs: &QuantumState) -> bool 701 | where [u8; (M + 7) / 8]: ,[u8; (N + 7) / 8]: { 702 | if N > M { is_eq(lhs, rhs) } 703 | else { is_eq(rhs, lhs) } 704 | } 705 | 706 | apply_monomorphized_fn!(other, is_eq_unsorted, self) 707 | } 708 | } 709 | 710 | fn make_swap_list(indicies: [usize; IN]) -> [(usize, usize); IN] { 711 | // Create a list of swap that we need to perform to move the qubits 712 | // listed in indicies to the front of the state. 713 | let mut index_map: [usize; N] = [0; N]; 714 | let mut swap_list = [(0, 0); IN]; 715 | for i in 0.. N { 716 | index_map[i] = i; 717 | } 718 | for (i, &idx) in indicies.iter().enumerate() { 719 | // Find idx in the list. 720 | let mut current_idx_idx = idx; 721 | while index_map[current_idx_idx] != idx { 722 | current_idx_idx = index_map[current_idx_idx]; 723 | } 724 | // Swap "i" and "current_idx_idx" 725 | let current_i = index_map[i]; 726 | index_map[i] = idx; // idx = index_map[current_idx_idx] 727 | index_map[current_idx_idx] = current_i; 728 | swap_list[i] = (i, current_idx_idx); 729 | } 730 | 731 | swap_list 732 | } 733 | 734 | #[derive(Copy, Clone)] 735 | struct ControlledUnitary 736 | where [u8; (N + 7) / 8]: { 737 | matrix: Matrix, 738 | /// Any of these bits 739 | control_any_true: Option>, 740 | control_all_true: BasisVector, 741 | /// All of these bits 742 | control_all_false: BasisVector, 743 | } 744 | 745 | impl ControlledUnitary 746 | where [u8; (N + 7) / 8]: { 747 | fn zero() -> Self { 748 | ControlledUnitary { 749 | matrix: Matrix::zero(), 750 | control_any_true: None, 751 | control_all_true: BasisVector::zero(), 752 | control_all_false: BasisVector::zero(), 753 | } 754 | } 755 | } 756 | 757 | impl QuantumState 758 | where [u8; (N + 7) / 8]: { 759 | /// Not actually a valid state! 760 | pub fn empty() -> QuantumState { 761 | QuantumState { 762 | decomposed: HashMap::new(), 763 | freed_qubits: BasisVector::zero(), 764 | reusable_qubits_ones: BasisVector::zero(), 765 | reusable_qubits_zeros: BasisVector::zero(), 766 | } 767 | } 768 | 769 | pub fn child(&self, iter: impl Iterator, Complex)>) -> QuantumState 770 | where [u8; (M + 7) / 8]: { 771 | let mut ret = QuantumState{ 772 | decomposed: HashMap::new(), 773 | freed_qubits: self.freed_qubits.resize::(), 774 | reusable_qubits_ones: self.reusable_qubits_ones.resize::(), 775 | reusable_qubits_zeros: self.reusable_qubits_zeros.resize::(), 776 | }; 777 | for (bv, val) in iter { 778 | ret.add(bv, val) 779 | } 780 | ret 781 | } 782 | 783 | fn update_reusability(&mut self) { 784 | let mut all_ones = BasisVector::max(); 785 | let mut all_zeros = BasisVector::zero(); 786 | for (bv, _) in self.iter() { 787 | all_ones &= bv; 788 | all_zeros |= bv; 789 | } 790 | 791 | let freed_zeros = self.freed_qubits & !all_zeros; 792 | let freed_ones = self.freed_qubits & all_ones; 793 | self.freed_qubits &= !(freed_zeros | freed_ones); 794 | self.reusable_qubits_zeros |= freed_zeros; 795 | self.reusable_qubits_ones |= freed_ones; 796 | } 797 | 798 | fn get(&self, idx: BasisVector) -> Complex { 799 | self.decomposed.get(&idx).copied().unwrap_or(Complex::zero()) 800 | } 801 | 802 | /// Note: This denormalizes 803 | pub fn set(&mut self, idx: BasisVector, val: Complex) { 804 | if val == Complex::zero() { 805 | self.decomposed.remove(&idx); 806 | } 807 | else { 808 | self.decomposed.insert(idx, val); 809 | } 810 | } 811 | 812 | /// Note: Does not preserve normalization 813 | pub fn add(&mut self, idx: BasisVector, val: Complex) { 814 | if val == Complex::zero() { return }; 815 | let new_val = self.get(idx) + val; 816 | self.set(idx, new_val); 817 | } 818 | 819 | pub fn iter<'a>(&'a self) -> impl Iterator, Complex)> + 'a { 820 | self.decomposed.iter().map(|(&bv, &val)| (bv, val)) 821 | } 822 | 823 | /// Indicies in order of low to high bits (when using them to index matrix cols) 824 | fn apply_controlled_unitaries_fixed( 825 | &self, 826 | unitaries: [ControlledUnitary; UNN], 827 | indicies: [usize; simple_log_2(UN)], 828 | ) -> Box { 829 | // TOOD: Make sure multiple unitaries should never be applied simultaneously... 830 | 831 | // TODO: Deduplicate with apply_fixed_unitary 832 | 833 | let swap_list = make_swap_list::(indicies); 834 | 835 | // Create an iterator with those swaps applied 836 | let swapped_iter = self.iter().map(|(pre_bv, val)| { 837 | let mut bv = pre_bv; 838 | for &(i, j) in &swap_list { 839 | let bits_xor = bv[i] ^ bv[j]; 840 | bv.set(i, bits_xor ^ bv[i]); 841 | bv.set(j, bits_xor ^ bv[j]); 842 | } 843 | (pre_bv, bv, val) 844 | }); 845 | 846 | // Apply the unitary to every element of that iterartor 847 | // which passes the control test (just return the elements that fail) 848 | let applied_iter = maybe_flat_map(swapped_iter, |(pre_bv, bv, val)| { 849 | for unitary in &unitaries { 850 | if let Some(control_true) = unitary.control_any_true { 851 | if pre_bv & control_true == BasisVector::zero() { 852 | continue; 853 | } 854 | } 855 | if (pre_bv & unitary.control_all_false) != BasisVector::zero() { 856 | continue; 857 | } 858 | if (pre_bv & unitary.control_all_true) != unitary.control_all_true { 859 | continue; 860 | } 861 | 862 | // Apply this unitary 863 | 864 | // Multiply bv by u identity 865 | 866 | // The basis vector picks out a column in that unitary. 867 | // The column is of the form [0 block][u column][0 block]. 868 | // So we need to return a new iterator of the basis vectors 869 | // for u_column * val_u_column * val. 870 | let index_column = bv % UN; 871 | let base_basis_vector = bv - index_column; 872 | let u = unitary.matrix; 873 | // The problem is just that the split indicies are out of order. 874 | // TODO: For tomorrow, audit all matricies and index orders and controls... 875 | return Ok(u[index_column].into_iter().enumerate().map(move |(i_col, val_u_col)| { 876 | let bv_col = (base_basis_vector + i_col).into(); 877 | let val = val_u_col * val; 878 | (bv_col, val) 879 | })) 880 | } 881 | 882 | // None of the matricies control bits like this, just emit the original bv. 883 | return Err((bv, val)); 884 | }); 885 | 886 | // Swap the basis elements back 887 | let output_iter = applied_iter.map(|(mut bv, val): (BasisVector, _)| { 888 | for &(i, j) in swap_list.iter().rev() { 889 | let bits_xor = bv[i] ^ bv[j]; 890 | bv.set(i, bits_xor ^ bv[i]); 891 | bv.set(j, bits_xor ^ bv[j]); 892 | } 893 | (bv, val) 894 | }); 895 | 896 | // Sum up the elements of the iterator 897 | Box::new(self.child(output_iter)) 898 | } 899 | 900 | /// Indicies in order of low to high bits (when using them to index matrix cols) 901 | pub fn apply_fixed_unitary( 902 | &self, 903 | u: &Matrix, 904 | indicies: [usize; simple_log_2(UN)], 905 | ) -> Box { 906 | let swap_list = make_swap_list::(indicies); 907 | 908 | // Create an iterator with those swaps applied 909 | let swapped_iter = self.iter().map(|(pre_bv, val)| { 910 | let mut bv = pre_bv; 911 | for &(i, j) in &swap_list { 912 | let bits_xor = bv[i] ^ bv[j]; 913 | bv.set(i, bits_xor ^ bv[i]); 914 | bv.set(j, bits_xor ^ bv[j]); 915 | } 916 | (bv, val) 917 | }); 918 | 919 | // Apply the unitary to every element of that iterartor 920 | // which passes the control test (just return the elements that fail) 921 | let applied_iter = swapped_iter.flat_map(|(bv, val)| { 922 | // Multiply bv by u identity 923 | 924 | // The basis vector picks out a column in that unitary. 925 | // The column is of the form [0 block][u column][0 block]. 926 | // So we need to return a new iterator of the basis vectors 927 | // for u_column * val_u_column * val. 928 | let index_column = bv % UN; 929 | let base_basis_vector = bv - index_column; 930 | u[index_column].into_iter().enumerate().map(move |(i_col, val_u_col)| { 931 | let bv_col = (base_basis_vector + i_col).into(); 932 | let val = val_u_col * val; 933 | (bv_col, val) 934 | }) 935 | }); 936 | 937 | // Swap the basis elements back 938 | let output_iter = applied_iter.map(|(mut bv, val): (BasisVector, _)| { 939 | for &(i, j) in swap_list.iter().rev() { 940 | let bits_xor = bv[i] ^ bv[j]; 941 | bv.set(i, bits_xor ^ bv[i]); 942 | bv.set(j, bits_xor ^ bv[j]); 943 | } 944 | (bv, val) 945 | }); 946 | 947 | Box::new(self.child(output_iter)) 948 | } 949 | 950 | #[cfg(feature = "future")] 951 | /// Split basis vectors into two sets, measure to decide which 952 | /// set to keep, and throw out the other set. 953 | fn measure_split(&mut self, partition: impl Fn(BasisVector) -> bool) -> bool { 954 | let prob_true: f64 = self 955 | .decomposed 956 | .iter() 957 | .filter(|&(&bv, _): &(&BasisVector,&Complex)| partition(bv)) 958 | .map(|(_, val)| val.norm_squared()) 959 | .sum(); 960 | 961 | let measure = prob_true >= rand(); 962 | let prob = if measure { prob_true } else { 1.0 - prob_true }; 963 | self.decomposed.drain_filter(|basis, val| { 964 | *val = *val / c!(prob.sqrt()); 965 | // TODO: Sign? 966 | partition(*basis) != measure 967 | }).for_each(drop); 968 | 969 | self.update_reusability(); 970 | measure 971 | } 972 | 973 | // I'd have seperate "add qubit" and "remove qubit" 974 | // functions, but I couldn't make const generics happy 975 | // in it's current state, so instead this is a shared 976 | // function that can go in either direction. 977 | // 978 | // Adds |0> qubits on the end, or removes qubits from 979 | // the end... err... I'm not sure the removal procedure 980 | // makes much sense right now because of interference... 981 | // (Also I'm doing this when tired, maybe it does, or 982 | // maybe none of this makes sense) 983 | fn resize(&self) -> QuantumState 984 | where [u8; (M + 7) / 8]:, { 985 | let out_iter = self 986 | .iter() 987 | .map(|(bv, val)| (bv.resize::(), val)); 988 | self.child(out_iter) 989 | } 990 | 991 | #[cfg(feature = "future")] 992 | fn renormalize(&mut self) { 993 | let prob: f64 = self.decomposed.values().map(|v| v.norm_squared()).sum(); 994 | self.decomposed.values_mut().for_each(|v| { *v = *v / c!(prob); }); 995 | } 996 | } 997 | 998 | fn rand() -> f64{ 999 | extern "C" { 1000 | fn drand48() -> f64; 1001 | } 1002 | unsafe{ drand48() } 1003 | } 1004 | 1005 | #[macro_export] 1006 | macro_rules! c { 1007 | ((i $val:expr)) => ($crate::c!(i $val)); 1008 | (i $val:expr) => ($crate::qcomp::Complex([0.0, $val])); 1009 | (($real:expr, i $imag:expr)) => ($crate::c!($real, i $imag)); 1010 | ($real:expr, i $imag:expr) => ($crate::qcomp::Complex([$real, $imag])); 1011 | ($val:expr) => ($crate::qcomp::Complex([$val, 0.0])); 1012 | } 1013 | 1014 | macro_rules! matrix_c { 1015 | ($([$($tks:tt),*]),*) => { 1016 | matrix![ 1017 | $( 1018 | [ 1019 | $(c!($tks),)* 1020 | ], 1021 | )* 1022 | ] 1023 | } 1024 | } 1025 | 1026 | #[cfg(test)] 1027 | macro_rules! state { 1028 | ($n:expr, $($basis_vec:expr => $val:expr),*) => {{ 1029 | let mut state = $crate::qcomp::QuantumState::<$n>::empty(); 1030 | $( 1031 | state.set($basis_vec.into(), $val); 1032 | )* 1033 | Box::new(state) as Box 1034 | }} 1035 | } 1036 | 1037 | fn maybe_flat_map( 1038 | mut iter: impl Iterator, 1039 | mut f: impl FnMut(I) -> Result 1040 | ) -> impl Iterator 1041 | where OI: Iterator { 1042 | fn apply( 1043 | maybe_oi: &mut Option, 1044 | iter: &mut impl Iterator, 1045 | f: &mut impl FnMut(I) -> Result 1046 | ) -> Option 1047 | where OI: Iterator { 1048 | // If there's an active iterator returned by an Ok(inner_iter), get the next elem 1049 | // from that. 1050 | if let Some(item) = maybe_oi.as_mut().and_then(|oi| oi.next()) { 1051 | return Some(item) 1052 | } else { *maybe_oi = None } 1053 | 1054 | // Otherwise either get the next elem, or the next active iterator 1055 | match f(iter.next()?) { 1056 | Ok(oi) => { 1057 | *maybe_oi = Some(oi); 1058 | apply(maybe_oi, iter, f) 1059 | } 1060 | Err(o) => Some(o) 1061 | } 1062 | } 1063 | 1064 | let mut maybe_oi = None; 1065 | std::iter::from_fn(move || apply(&mut maybe_oi, &mut iter, &mut f)) 1066 | } 1067 | 1068 | #[test] 1069 | fn test_find_one() { 1070 | let bv: BasisVector<15> = BasisVector::from(0b10u64); 1071 | assert_eq!(bv.find_one(), Some(1)); 1072 | let bv: BasisVector<15> = BasisVector::from(0b10_0000_0000u64); 1073 | assert_eq!(bv.find_one(), Some(9)); 1074 | } 1075 | #[test] 1076 | fn test_leading_ones() { 1077 | fn slow_leading_ones(bv: BasisVector) -> usize 1078 | where [u8; (N + 7)/8]: { 1079 | let mut out = 0; 1080 | for i in 0.. N { 1081 | if bv[N - i - 1] == false { 1082 | break; 1083 | } 1084 | out += 1; 1085 | } 1086 | return out; 1087 | } 1088 | let bv: BasisVector<15> = BasisVector::from(0b111_1000_1111_1111u64); 1089 | assert_eq!(bv.leading_ones(), slow_leading_ones(bv), "{bv:?}"); 1090 | assert_eq!(slow_leading_ones(bv), 4); 1091 | 1092 | let bv: BasisVector<25> = BasisVector::from(0x1fffe00); 1093 | assert_eq!(bv.leading_ones(), slow_leading_ones(bv)); 1094 | assert_eq!(slow_leading_ones(bv), 16); 1095 | } -------------------------------------------------------------------------------- /src/qcomp_explainable.rs: -------------------------------------------------------------------------------- 1 | /// A wrapper around qcomp that tries to help make it 2 | /// "explainable" and tries to expose a cleaner api 3 | /// (with the const related shenanigans hidden) 4 | 5 | use std::mem::replace; 6 | 7 | use crate::qcomp::{ 8 | simple_log_2, 9 | BasisVector, 10 | Complex, 11 | DynamicMatrix, 12 | Matrix, 13 | QuantumSimulator, 14 | QuantumState, 15 | }; 16 | 17 | #[derive(Clone, Debug, Default)] 18 | pub struct AllocInfo { 19 | // TODO: Don't tie this to chess types... make generic? 20 | pub piece: Option, 21 | name: String, 22 | purpose: String, 23 | freed: bool, 24 | } 25 | 26 | pub struct SimpleQuantumState { 27 | pub state: Box, 28 | // Todo: Efficient type for cloning? 29 | pub qbits: Vec, 30 | } 31 | 32 | impl SimpleQuantumState { 33 | pub fn new(bv: BasisVector, mut name_fn: impl FnMut(usize) -> String) -> Self 34 | where [u8; (N + 7) / 8]: { 35 | let mut qbits = Vec::with_capacity(N); 36 | for i in 0.. N { 37 | qbits.push(AllocInfo { 38 | piece: None, 39 | name: name_fn(i), 40 | purpose: "".into(), 41 | freed: false, 42 | }); 43 | } 44 | let mut state = QuantumState::::empty(); 45 | state.set(bv, Complex([1.0, 0.0])); 46 | SimpleQuantumState { 47 | state: Box::new(state), 48 | qbits 49 | } 50 | } 51 | 52 | pub fn apply_unitary( 53 | &mut self, 54 | u: Matrix, 55 | indicies: [usize; simple_log_2(UN)], 56 | ) { 57 | self.print_unitary(u, indicies); 58 | 59 | self.state = self.state.apply_unitary_dynamic( 60 | DynamicMatrix::new(&u), 61 | &indicies, 62 | ); 63 | } 64 | 65 | #[cfg(feature = "future")] 66 | pub fn apply_unitary_and_clone( 67 | &self, 68 | u: Matrix, 69 | indicies: [usize; simple_log_2(UN)], 70 | ) -> Self { 71 | self.print_unitary(u, indicies); 72 | 73 | let state = self.state.apply_unitary_dynamic( 74 | DynamicMatrix::new(&u), 75 | &indicies, 76 | ); 77 | 78 | SimpleQuantumState { 79 | state, 80 | qbits: self.qbits.clone() 81 | } 82 | } 83 | 84 | pub fn apply_controlled_unitaries( 85 | &mut self, 86 | us: [(&Matrix, Option<&[usize]>, &[usize], &[usize]); UNN], 87 | indicies: [usize; simple_log_2(UN)] 88 | ) { 89 | self.print_controlled_unitaries(us, indicies); 90 | 91 | let dyn_us = 92 | us.map(|(u, c_any_t, c_all_t, cf)| (DynamicMatrix::new(u), c_any_t, c_all_t, cf)); 93 | self.state = self.state.apply_controlled_unitaries_dynamic(&dyn_us, &indicies); 94 | } 95 | 96 | pub fn apply_controlled_unitaries_and_clone( 97 | &self, 98 | us: [(&Matrix, Option<&[usize]>, &[usize], &[usize]); UNN], 99 | indicies: [usize; simple_log_2(UN)] 100 | ) -> Self { 101 | self.print_controlled_unitaries(us, indicies); 102 | 103 | let dyn_us = 104 | us.map(|(u, c_any_t, c_all_t, cf)| (DynamicMatrix::new(u), c_any_t, c_all_t, cf)); 105 | let state = self.state.apply_controlled_unitaries_dynamic(&dyn_us, &indicies); 106 | 107 | SimpleQuantumState { 108 | state, 109 | qbits: self.qbits.clone() 110 | } 111 | } 112 | 113 | pub fn measure(&mut self, qbit: usize) -> bool { 114 | println!("Measuring {} ({})", self.name(qbit), self.qbits[qbit].purpose); 115 | let (out, prob) = self.state.measure(qbit); 116 | println!("\tValue: {out}, which happens {:.2}% of the time", prob * 100.0); 117 | out 118 | } 119 | 120 | /// Get a new qubit. If no qubit's have been freed and we have 121 | /// not yet hit our maximum, this returns qubit n where n qubits 122 | /// have been previously allocated (useful if you want non 123 | /// ancilla qubits at fixed positions). After qubits have been 124 | /// freed all bets are off. 125 | pub fn alloc_qubit(&mut self, piece: Option, name: String, purpose: String) -> usize { 126 | let state = replace(&mut self.state, Box::new(PlaceHolderSimulator)); 127 | let (state, qbit) = state.alloc_qubit(); 128 | self.state = state; 129 | self.record_alloc_qbit(qbit, piece, name, purpose); 130 | qbit 131 | } 132 | 133 | pub fn alloc_qubit_and_clone(&self, piece: Option, name: String, purpose: String) -> (Self, usize) { 134 | let (state, qbit) = self.state.alloc_qubit_ref(); 135 | let mut new_state = SimpleQuantumState{ 136 | state, 137 | qbits: self.qbits.clone() 138 | }; 139 | new_state.record_alloc_qbit(qbit, piece, name, purpose); 140 | 141 | (new_state, qbit) 142 | } 143 | 144 | /// Tell the implementation that *we* are done with this qubit, 145 | /// the qubit does not need to be in a |0>/product/... state. 146 | pub fn free_qubit(&mut self, qbit: usize) { 147 | self.state.free_qubit(qbit); 148 | self.qbits[qbit].freed = true; 149 | } 150 | 151 | pub fn print_state(&self) { 152 | fn print_state_inner(state: &QuantumState, qbits: &[AllocInfo]) 153 | where [u8; (N + 7) / 8]: { 154 | println!("QuantumState<{N}>"); 155 | for i in 64.. N { 156 | println!("\tqbit {} is {}: {} (capturing {:?})", i, qbits[i].name, qbits[i].purpose, qbits[i].piece); 157 | } 158 | println!("\tfreed_qubits: {:?}", state.freed_qubits); 159 | println!("\treusable_qubits_zeros: {:?}", state.reusable_qubits_zeros); 160 | println!("\treusable_qubits_ones: {:?}", state.reusable_qubits_ones); 161 | if state.decomposed.len() > 64 { 162 | println!("{} states ", state.decomposed.len()); 163 | } 164 | else { 165 | for (bv, c) in state.iter() { 166 | println!("\t{bv:?} {c:?}"); 167 | } 168 | } 169 | } 170 | 171 | crate::apply_monomorphized_fn!(&self.state, print_state_inner, &self.qbits); 172 | } 173 | 174 | fn record_alloc_qbit(&mut self, qbit: usize, piece: Option, name: String, purpose: String) { 175 | let info = AllocInfo{ piece, name, purpose, freed: false }; 176 | if qbit == self.qbits.len() { 177 | self.qbits.push(info) 178 | } else { 179 | self.qbits[qbit] = info; 180 | } 181 | } 182 | 183 | fn print_controlled_unitaries( 184 | &self, 185 | us: [(&Matrix, Option<&[usize]>, &[usize], &[usize]); UNN], 186 | indicies: [usize; simple_log_2(UN)] 187 | ) { 188 | println!("\nApplying a set of {UNN} controlled unitaries"); 189 | println!("\tTo: {}", self.qbit_set_str(&indicies)); 190 | for (mat, control_any_true, control_all_true, control_all_false) in &us { 191 | println!("\tControlled by:"); 192 | if let Some(control_true) = control_any_true { 193 | if control_true.len() == 0 { 194 | println!("\t\tNever"); 195 | } 196 | else { 197 | println!("\t\tAny of: {}", self.qbit_set_str(control_true)); 198 | } 199 | } 200 | if control_all_true.len() != 0 { 201 | println!("\t\tAll of: {}", self.qbit_set_str(control_all_true)); 202 | } 203 | if control_all_false.len() != 0 { 204 | println!("\t\tNone of: {}", self.qbit_set_str(control_all_false)); 205 | } 206 | println!("\tApplying\n{}", mat_str(**mat, "\t\t")); 207 | } 208 | } 209 | 210 | fn print_unitary( 211 | &self, 212 | u: Matrix, 213 | indicies: [usize; simple_log_2(UN)], 214 | ) { 215 | println!("\nApplying\n{}\n\tto qubits {}", 216 | mat_str(u, "\t\t"), 217 | self.qbit_set_str(&indicies), 218 | ); 219 | } 220 | 221 | fn name(&self, qbit: usize) -> &str { 222 | &self.qbits[qbit].name 223 | } 224 | 225 | fn qbit_set_str(&self, qbits: &[usize]) -> String { 226 | let mut out = String::new(); 227 | for (i, &bit) in qbits.iter().enumerate() { 228 | out += self.name(bit); 229 | if i+1 != qbits.len() { 230 | out += ", " 231 | } 232 | } 233 | out 234 | } 235 | } 236 | 237 | fn mat_str(mat: Matrix, prefix: &str) -> String { 238 | let fmt_mat = mat.map(|elem| format!("{:?} ", elem)); 239 | let mut col_widths = [0; N]; 240 | for (i, col) in fmt_mat.column_iter().enumerate() { 241 | col_widths[i] = col.iter().map(|x| x.len()).max().unwrap(); 242 | } 243 | let mut out = String::with_capacity(4*N); 244 | for i in 0.. N { 245 | out += prefix; 246 | for j in 0.. N { 247 | out += &format!("{0:^1$}", fmt_mat[(i, j)], col_widths[j]); 248 | } 249 | out += "\n"; 250 | } 251 | out 252 | } 253 | 254 | #[derive(Debug)] 255 | struct PlaceHolderSimulator; 256 | impl QuantumSimulator for PlaceHolderSimulator { 257 | fn apply_unitary_dynamic( 258 | &self, 259 | _u: DynamicMatrix, 260 | _indicies: &[usize], 261 | ) -> Box { 262 | unreachable!() 263 | } 264 | fn apply_controlled_unitaries_dynamic( 265 | &self, 266 | // Matrix, control true, control false. 267 | _unitaries: &[( 268 | DynamicMatrix, 269 | Option<&[usize]>, 270 | &[usize], 271 | &[usize], 272 | )], 273 | _indicies: &[usize], 274 | ) -> Box { 275 | unreachable!() 276 | } 277 | fn measure(&mut self, _qubit: usize) -> (bool, f64) { 278 | unreachable!() 279 | } 280 | fn state_size(&self) -> usize { 281 | unreachable!() 282 | } 283 | fn alloc_qubit(self: Box) -> (Box, usize) { 284 | unreachable!() 285 | } 286 | fn alloc_qubit_ref(&self) -> (Box, usize) { 287 | unreachable!() 288 | } 289 | fn free_qubit(&mut self, _qubit: usize) { 290 | unreachable!() 291 | } 292 | fn as_any(&self) -> &dyn std::any::Any { 293 | unreachable!() 294 | } 295 | fn eq_any(&self, _other: &dyn QuantumSimulator) -> bool { 296 | unreachable!() 297 | } 298 | } -------------------------------------------------------------------------------- /src/term.rs: -------------------------------------------------------------------------------- 1 | use qc::{ 2 | get_probs, 3 | ChessState, 4 | Row, 5 | Column, 6 | Move, 7 | Square 8 | }; 9 | 10 | fn state_string(state: &ChessState) -> String { 11 | use std::fmt::Write; 12 | // 8 rows * 4 height * 8 columns * 7 width + 8 newlines 13 | let mut out = String::with_capacity(8 * 4 * 8 * 7 + 8); 14 | 15 | let pieces = state.pieces; 16 | let probs = get_probs(state); 17 | 18 | out += " "; 19 | for i in 0.. 8 * 4 { 20 | if i % 4 == 2 { 21 | out.push((b'A' + (i / 4)) as char); 22 | } 23 | else { 24 | out.push(' '); 25 | } 26 | } 27 | out += "\n "; 28 | for _ in 0.. 8 * 4 + 1 { 29 | out += "-"; 30 | } 31 | out += "\n"; 32 | for row in 0.. 8 { 33 | // Piece line 34 | out.push((b'1' + row as u8) as char); 35 | out.push(' '); 36 | for col in 0.. 8 { 37 | let row = Row::from_idx(row).unwrap(); 38 | let col = Column::from_idx(col).unwrap(); 39 | let sq = Square{ row, col }; 40 | let piece = pieces[sq.get_idx()]; 41 | let c = match piece { 42 | Some(p) => p.symbol(), 43 | None => ' ', 44 | }; 45 | write!(out, "| {} ", c).unwrap(); 46 | } 47 | out += "|\n "; 48 | // Prob line 49 | for col in 0.. 8 { 50 | let prob = probs[row * 8 + col]; 51 | assert!(prob < 1.001); 52 | if prob != 0.0 && prob <= 0.999 { 53 | // let s = format!("{:.2}", prob); 54 | write!(out, "|.{:^2}", (prob * 100.).round()).unwrap(); 55 | } 56 | else { 57 | out += "| "; 58 | } 59 | } 60 | // End prob line + padding 61 | out += "|\n "; 62 | for _ in 0.. 8 * 4 + 1 { 63 | out += "-"; 64 | } 65 | out += "\n"; 66 | } 67 | 68 | out 69 | } 70 | 71 | #[cfg(feature = "future")] 72 | fn board_string(pieces: [Option; 64]) -> String { 73 | let mut out = String::with_capacity(64 + 8); 74 | for row in 0.. 8 { 75 | for col in 0.. 8 { 76 | let row = Row::from_idx(row).unwrap(); 77 | let col = Column::from_idx(col).unwrap(); 78 | let sq = Square{ row, col }; 79 | let piece = pieces[sq.get_idx()]; 80 | let c = match piece { 81 | Some(p) => p.symbol(), 82 | None => ' ', 83 | }; 84 | out.push(' '); 85 | out.push(c); 86 | } 87 | out.push('\n'); 88 | } 89 | out 90 | } 91 | 92 | fn fake_main() -> Result<(), std::io::Error> { 93 | use std::io::{self, BufRead, prelude::*}; 94 | use std::str::FromStr; 95 | use std::fs::File; 96 | 97 | let seed = u64::from_str(&std::env::args().nth(1).unwrap()).unwrap(); 98 | unsafe { 99 | extern "C" { fn srand48(x: u64); } 100 | srand48(seed); 101 | } 102 | 103 | let mut outfile = File::create("moves_out").unwrap(); 104 | 105 | let mut state = ChessState::new_game(); 106 | let input = io::BufReader::new(io::stdin()); 107 | println!("{}", state_string(&state)); 108 | println!("White to start the game"); 109 | for line in input.lines() { 110 | let mut src: &str = &*(line?); 111 | writeln!(outfile, "{}", src).unwrap(); 112 | let chess_move = match Move::parse(&mut src) { 113 | Ok(cm) => cm, 114 | Err(e) => { 115 | println!("{}\nTry again", e); 116 | continue 117 | } 118 | }; 119 | match state.apply_move(chess_move) { 120 | Ok((new_state, Some(m))) => { 121 | state = new_state; 122 | println!("Success; m{}", if m {"1"} else {"0"}) 123 | }, 124 | Ok((new_state, None)) => { 125 | state = new_state; 126 | println!("Success") 127 | }, 128 | Err(e) => println!("{}\nTry again", e) 129 | } 130 | 131 | let next = if state.is_white_turn { "White" } else { "Black" }; 132 | state.occupancy.print_state(); 133 | println!("{}", state_string(&state)); 134 | println!("{} to play", next); 135 | } 136 | 137 | Ok(()) 138 | } 139 | 140 | fn main() { 141 | use std::thread; 142 | let builder = thread::Builder::new() 143 | .name("reductor".into()) 144 | .stack_size(1024 * 1024 * 1024); // 1GB of stack space 145 | 146 | let handler = builder.spawn(|| { 147 | fake_main().unwrap() 148 | }).unwrap(); 149 | 150 | handler.join().unwrap(); 151 | } --------------------------------------------------------------------------------