File size: 3,442 Bytes
eef9104
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# *******************************************************************************
# 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
# *******************************************************************************

"""Proof-carrying scattering synthesis.

The compiler admits a gate list, checks the translation orbit, exponentiates
only the admitted local terms, and binds a certificate to the admitted list.
"""

from __future__ import annotations

import numpy as np

from cpsc.analyzer import CompilationRejected, Gate, SymmetryAnalyzer, SymmetryMode
from cpsc.certificate import Certificate, bind_certificate
from cpsc.channel import evolve_trotter, x_parity_leakage, z_parity_leakage
from cpsc.hamiltonian import magnetization_of, parity_of


def translation_orbit_closed(gates: list[Gate], n: int) -> str | None:
    """A translation-invariant schedule must include every cyclic image.

    ZZ[i, i+1] is closed only if every bond appears. A lone transverse flip
    is closed only if every site appears with the same kind. Global gates
    with empty support are already invariant.
    """
    names = {(g.kind, g.qubits) for g in gates}
    for kind, qubits in list(names):
        if not qubits:
            continue
        for shift in range(n):
            image = tuple((q + shift) % n for q in qubits)
            if (kind, image) not in names and (kind, image[::-1]) not in names:
                return (
                    f"{kind}{qubits} is not closed under translation; "
                    f"missing image {image}"
                )
    return None


def compile_scattering(
    n: int,
    initial_basis: int,
    gates: list[Gate],
    *,
    time: float,
    steps: int = 8,
    j: float = 1.0,
    lam: float = 0.0,
    require_translation: bool = True,
) -> dict:
    if steps < 1:
        raise ValueError("steps must be positive")
    mode = SymmetryMode.ISING if lam == 0.0 else SymmetryMode.SPIN_FLIP
    analyzer = SymmetryAnalyzer(n, mode=mode)
    admitted = analyzer.admit(gates)
    if require_translation:
        reason = translation_orbit_closed(admitted, n)
        if reason is not None:
            raise CompilationRejected(reason)
    conserve_mag = mode is SymmetryMode.ISING
    state = evolve_trotter(
        n,
        initial_basis,
        admitted,
        time=time,
        steps=steps,
        j=j,
        lam=lam,
        conserve_magnetization=conserve_mag,
    )
    certificate = bind_certificate(admitted, mode=mode.value, n=n, lam=lam, j=j, time=time, steps=steps)
    z_leak = z_parity_leakage(state, n, parity_of(initial_basis, n))
    x_leak = x_parity_leakage(state, n, initial_basis)
    return {
        "mode": mode.value,
        "admitted": admitted,
        "state": state,
        "certificate": certificate,
        "z_parity_leakage": z_leak,
        "x_parity_leakage": x_leak,
        "magnetization": magnetization_of(initial_basis, n) if conserve_mag else None,
    }