File size: 4,278 Bytes
9e551a1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// Tensor Roll — Recursive CUDA-Q Model Quantizer
// Copyright (C) 2026 SnapKitty Collective
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// Genuine Q# operations for the tensor_roll kernel family.
// These mirror cudaq/tensor_roll_kernels.py across the process/IR boundary
// documented in docs/BOUNDARIES.md: CUDA-Q kernels emit QIR/MLIR on the
// CUDA-Q side; this Q# source is the equivalent family expressed for the
// Q# toolchain. There is no direct CUDA-Q <-> Q# linkage — the boundary is
// a real serialized artifact (QIR bitcode / measurement-counts JSON),
// never an invented API.
//
// NOTE: not compiled or executed in this repository's test environment
// (no Q# toolchain present). Every execution claim in tensor-roll refers
// to the classical qsim-classical simulator, explicitly labeled as such.

namespace TensorRoll {
    open Microsoft.Quantum.Canon;
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Measurement;
    open Microsoft.Quantum.Math;
    open Microsoft.Quantum.Convert;
    open Microsoft.Quantum.Arrays;

    /// Encodes a classical tensor block into a qubit register (angle encoding).
    operation EncodeTensorBlock(block : Double[], register : Qubit[]) : Unit {
        let n = Length(block) < Length(register) ? Length(block) | Length(register);
        for i in 0..n - 1 {
            Ry(2.0 * ArcTan(block[i]), register[i]);
        }
    }

    /// Applies per-qubit RY rotations — the "roll" of the register.
    operation RollRegister(register : Qubit[], angles : Double[]) : Unit {
        for i in 0..Length(register) - 1 {
            Ry(angles[i % Length(angles)], register[i]);
        }
    }

    /// Nearest-neighbour CNOT ladder over the register.
    operation EntangleRoll(register : Qubit[]) : Unit {
        for i in 0..Length(register) - 2 {
            Controlled X([register[i]], register[i + 1]);
        }
    }

    /// Recursive kernel: each level rotates and entangles the active subset,
    /// measures the outer qubit, resets it, and recurses on the remainder.
    /// Q# operations may call themselves — this is genuine recursion.
    /// Returns one measurement Result per recursion level.
    operation RecursiveRoll(register : Qubit[], angles : Double[], depth : Int) : Result[] {
        if depth <= 0 or Length(register) == 0 {
            return [];
        }
        RollRegister(register, angles);
        EntangleRoll(register);
        let outer = register[Length(register) - 1];
        let outcome = MResetZ(outer);
        let rest = RecursiveRoll(register[0..Length(register) - 2], angles, depth - 1);
        return [outcome] + rest;
    }

    /// Measures the register in the Z basis, resetting each qubit.
    operation MeasureRoll(register : Qubit[]) : Result[] {
        mutable results = [];
        for q in register {
            set results += [MResetZ(q)];
        }
        return results;
    }

    /// Classical reconstruction of amplitude estimates from measurement counts.
    /// Reconstruction is classical by construction — this function executes on
    /// the classical host, never on the quantum processor.
    function ReconstructBlock(counts : (Result[], Int)[], nQubits : Int) : Double[] {
        mutable total = 0;
        for (_, c) in counts {
            set total += c;
        }
        let dim = 1 <<< nQubits;
        mutable amps = [0.0, size = dim];
        for (bits, c) in counts {
            mutable idx = 0;
            for b in bits {
                set idx = idx * 2 + (b == One ? 1 | 0);
            }
            if idx < dim and total > 0 {
                set amps w/= idx <- Sqrt(IntAsDouble(c) / IntAsDouble(total));
            }
        }
        return amps;
    }

    @EntryPoint()
    operation Main() : Unit {
        // Small demonstration block; mirrors the 8x8-block smoke test that
        // tensor-roll qkernel runs classically via qsim.
        let block = [0.1, 0.4, 0.2, 0.8];
        use register = Qubit[4];
        EncodeTensorBlock(block, register);
        let rolled = RecursiveRoll(register, [0.3, 0.6, 0.2, 0.9], 2);
        let tail = MeasureRoll(register);
        Message($"recursive outcomes: {rolled}");
        Message($"tail outcomes: {tail}");
        ResetAll(register);
    }
}