tensor-roll / qsharp /TensorRoll.qs
SNAPKITTYWEST's picture
October 2026 main drop: mirror from GitHub
9e551a1 verified
Raw History Blame Contribute Delete
4.28 kB
// 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);
}
}