Download frontend/lib/mercury/cpsc.rb from Snapkitty/cpsc-qft: direct link, hf CLI and curl.
- Browser
- Download file 5.57 kB
-
https://huggingface.co/Snapkitty/cpsc-qft/resolve/main/frontend/lib/mercury/cpsc.rb
- Command line
-
hf download hf://Snapkitty/cpsc-qft/frontend/lib/mercury/cpsc.rb
-
curl -L -o cpsc.rb https://huggingface.co/Snapkitty/cpsc-qft/resolve/main/frontend/lib/mercury/cpsc.rb
5.57 kB
| # ******************************************************************************* | |
| # Copyright (c) 2026 Ahmad Ali Parr and others. | |
| # | |
| # This program and the accompanying materials are made available under the | |
| # terms of the CPSC Eclipse Strict Copyleft License, Version 1.0, which is | |
| # available in LICENSES/CPSC-ESCL-1.0.txt, or, at your option, under the | |
| # GNU Affero General Public License, Version 3 only, which is available in | |
| # LICENSES/AGPL-3.0.txt. Both options are strict copyleft. There is no | |
| # classpath exception and no permissive relicensing. | |
| # | |
| # SPDX-License-Identifier: LicenseRef-CPSC-ESCL-1.0 OR AGPL-3.0-only | |
| # ******************************************************************************* | |
| # Ruby mirror of the CPSC Python analyzer (src/cpsc/analyzer.py) plus a | |
| # Trotterized scattering evolution built as a Mercury circuit. | |
| # | |
| # Gate-kind semantics (identical to the Python original): | |
| # - "magnetization": diagonal in the Z basis; preserves sum Z and prod Z. | |
| # - "parity_even": preserves prod Z (Z2 parity) but may change sum Z. | |
| # - "illegal": odd X/Y support; changes Z-parity. Rejected. | |
| module Mercury | |
| module CPSC | |
| # Mercury gate -> analyzer kind. Mirrors analyzer.py's Gate kinds. | |
| KINDS = { | |
| i: "magnetization", z: "magnetization", s: "magnetization", | |
| t: "magnetization", sdg: "magnetization", tdg: "magnetization", | |
| rz: "magnetization", cz: "magnetization", rzz: "magnetization", | |
| cp: "magnetization", | |
| xx: "parity_even", | |
| x: "illegal", y: "illegal", h: "illegal", | |
| rx: "illegal", ry: "illegal", u: "illegal", | |
| cnot: "illegal", swap: "illegal", ccx: "illegal" | |
| }.freeze | |
| GateSpec = Struct.new(:name, :qubits, :kind) | |
| Rejection = Struct.new(:spec, :reason) | |
| class CompilationRejected < StandardError; end | |
| class SymmetryAnalyzer | |
| def initialize(n, conserve_magnetization: false) | |
| raise ArgumentError, "n must be positive" unless n.is_a?(Integer) && n >= 1 | |
| @n = n | |
| @conserve_magnetization = conserve_magnetization | |
| end | |
| def check(specs) | |
| specs.flat_map do |spec| | |
| reason = reject_reason(spec) | |
| reason ? [Rejection.new(spec, reason)] : [] | |
| end | |
| end | |
| def admit(specs) | |
| rejected = check(specs) | |
| unless rejected.empty? | |
| detail = rejected.map { |r| "#{r.spec.name}: #{r.reason}" }.join("; ") | |
| raise CompilationRejected, detail | |
| end | |
| specs.dup | |
| end | |
| private | |
| def reject_reason(spec) | |
| spec.qubits.each do |q| | |
| return "qubit #{q} outside lattice of size #{@n}" unless q.is_a?(Integer) && q.between?(0, @n - 1) | |
| end | |
| case spec.kind | |
| when "illegal" | |
| "odd X/Y support changes Z-parity; forbidden sector" | |
| when "parity_even", "magnetization" | |
| nil | |
| else | |
| "unknown gate kind #{spec.kind}" | |
| end | |
| end | |
| end | |
| # Classify a Mercury::Circuit::Op into a GateSpec for the analyzer. | |
| def self.classify(op) | |
| kind = KINDS.fetch(op.gate) { raise ArgumentError, "cannot classify #{op.gate}" } | |
| label = op.params.empty? ? op.gate.to_s.upcase : "#{op.gate}(#{op.params.map { |p| format('%.3f', p) }.join(',')})" | |
| GateSpec.new("#{label}[#{op.qubits.join(',')}]", op.qubits, kind) | |
| end | |
| # Run the analyzer over a whole circuit. Returns | |
| # { admitted: bool, rejections: [{gate, reason}] }. | |
| def self.analyze(circuit, conserve_magnetization: false) | |
| analyzer = SymmetryAnalyzer.new(circuit.n, conserve_magnetization: conserve_magnetization) | |
| specs = circuit.ops.reject { |op| op.gate == :measure }.map { |op| classify(op) } | |
| rejections = analyzer.check(specs) | |
| { | |
| admitted: rejections.empty?, | |
| rejections: rejections.map { |r| { gate: r.spec.name, reason: r.reason } } | |
| } | |
| end | |
| # First-order Trotter circuit for H = -J sum Z_i Z_{i+1} + lam sum X_i | |
| # (periodic), matching src/cpsc/hamiltonian.py. Per step dt: | |
| # prod_bonds exp(+i J dt Z_i Z_{i+1}) then prod_i exp(-i lam dt X_i). | |
| def self.trotter_circuit(n, j:, lam:, time:, steps:) | |
| raise ArgumentError, "steps must be >= 1" unless steps >= 1 | |
| dt = time.to_f / steps | |
| Circuit.new(n) do |c| | |
| steps.times do | |
| (0...n).each { |i| c.rzz(-2.0 * j * dt, i, (i + 1) % n) } | |
| (0...n).each { |i| c.rx(2.0 * lam * dt, i) } unless lam.zero? | |
| end | |
| end | |
| end | |
| def self.parity_of(basis, n) | |
| basis.to_s(2).count("1") & 1 | |
| end | |
| def self.magnetization_of(basis, n) | |
| mag = 0 | |
| n.times { |i| mag += ((basis >> i) & 1).zero? ? 1 : -1 } | |
| mag | |
| end | |
| # Probability mass outside the input Z2-parity sector. | |
| def self.leakage(amps, n, parity) | |
| wrong = 0.0 | |
| amps.each_with_index do |a, b| | |
| wrong += a.abs**2 if parity_of(b, n) != parity | |
| end | |
| wrong | |
| end | |
| # <psi|H|psi> for the transverse-field Ising H, computed directly. | |
| def self.energy_expectation(amps, n, j:, lam:) | |
| dim = 1 << n | |
| e = 0.0 | |
| dim.times do |s| | |
| p = amps[s].abs**2 | |
| next if p.zero? | |
| zz = 0.0 | |
| n.times do |i| | |
| zi = ((s >> i) & 1).zero? ? 1.0 : -1.0 | |
| zj = ((s >> ((i + 1) % n)) & 1).zero? ? 1.0 : -1.0 | |
| zz += zi * zj | |
| end | |
| e += p * (-j * zz) | |
| end | |
| unless lam.zero? | |
| x = 0.0 | |
| n.times do |i| | |
| bit = 1 << i | |
| dim.times { |s| x += (amps[s].conj * amps[s ^ bit]).real } | |
| end | |
| e += lam * x | |
| end | |
| e | |
| end | |
| end | |
| end | |