Skip to content

Commit f2865cd

Browse files
authored
Clean (#282)
* Remove unsafe transmute_copy, add debug_assert bounds, zero GPU buffers, reduce FFI boilerplate * Remove more unsafe, add debug_asserts to sparse stabs and GPU packing, fix Qulacs silent no-ops * Promote row_xor_assign aliasing guard to release-mode assert * Fix rustfmt formatting in gpu_stab.rs * Add Panics doc section for row_xor_assign to satisfy clippy
1 parent ee6bfd3 commit f2865cd

18 files changed

Lines changed: 271 additions & 425 deletions

File tree

crates/pecos-core/src/bitset.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,9 +378,7 @@ impl BitSet {
378378
for &index in indices {
379379
let word_idx = index / 64;
380380
let bit_idx = index % 64;
381-
if word_idx < self.words.len()
382-
&& (unsafe { *self.words.get_unchecked(word_idx) } & (1u64 << bit_idx)) != 0
383-
{
381+
if word_idx < self.words.len() && (self.words[word_idx] & (1u64 << bit_idx)) != 0 {
384382
count += 1;
385383
}
386384
}

crates/pecos-core/src/phase/quarter_phase.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ impl Phase for QuarterPhase {
4444
}
4545

4646
fn multiply(&self, other: &QuarterPhase) -> QuarterPhase {
47+
use QuarterPhase::{MinusI, MinusOne, PlusI, PlusOne};
48+
const VARIANTS: [QuarterPhase; 4] = [PlusOne, MinusOne, PlusI, MinusI];
49+
4750
let lhs = *self as u8;
4851
let rhs = *other as u8;
4952

@@ -53,10 +56,7 @@ impl Phase for QuarterPhase {
5356
// XOR imaginary parts
5457
let imaginary = (lhs ^ rhs) & 0b10;
5558

56-
let result = real | imaginary;
57-
58-
// Cast back to Phase
59-
unsafe { std::mem::transmute(result) }
59+
VARIANTS[(real | imaginary) as usize]
6060
}
6161
}
6262

crates/pecos-core/src/phase/sign.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,12 @@ impl Phase for Sign {
3131
*self
3232
}
3333

34-
/// Multiplies two `Sign` values using XOR (superfast).
34+
/// Multiplies two `Sign` values using XOR.
3535
fn multiply(&self, other: &Self) -> Self {
36-
unsafe { std::mem::transmute((*self as u8) ^ (*other as u8)) }
36+
match (*self as u8) ^ (*other as u8) {
37+
0 => Sign::PlusOne,
38+
_ => Sign::MinusOne,
39+
}
3740
}
3841
}
3942

@@ -51,8 +54,10 @@ impl TryFrom<QuarterPhase> for Sign {
5154

5255
impl From<Sign> for QuarterPhase {
5356
fn from(sign: Sign) -> QuarterPhase {
54-
// Safe because `Sign` variants map directly to `Phase` variants
55-
unsafe { std::mem::transmute(sign as u8) }
57+
match sign {
58+
Sign::PlusOne => QuarterPhase::PlusOne,
59+
Sign::MinusOne => QuarterPhase::MinusOne,
60+
}
5661
}
5762
}
5863

crates/pecos-cppsparsestab/src/lib.rs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,20 +80,10 @@ pub struct CppSparseStab {
8080
num_qubits: usize,
8181
}
8282

83-
// SAFETY: CppSparseStab can be safely sent between threads because:
84-
// 1. The C++ StateWrapper manages its own memory properly
85-
// 2. Each instance is used by only one thread at a time
86-
// 3. The underlying C++ code has no shared state between instances
87-
// 4. cxx::UniquePtr provides exclusive ownership
83+
// SAFETY: CppSparseStab can be moved between threads. Each instance owns its
84+
// C++ state exclusively via cxx::UniquePtr, and all access is through &mut self.
8885
unsafe impl Send for CppSparseStab {}
8986

90-
// SAFETY: CppSparseStab can be safely shared between threads because:
91-
// 1. The underlying C++ StateWrapper is thread-safe for concurrent read access
92-
// 2. Each instance maintains its own independent state
93-
// 3. No global/shared mutable state is accessed
94-
// 4. cxx::UniquePtr ensures exclusive ownership semantics
95-
unsafe impl Sync for CppSparseStab {}
96-
9787
impl CppSparseStab {
9888
/// Get mutable access to the underlying C++ state.
9989
///

crates/pecos-cuquantum/src/stabilizer.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,11 @@ impl CuFrameSimulator {
162162
"Failed to allocate M table: CUDA error {cuda_status}"
163163
)));
164164
}
165+
166+
// Zero all GPU buffers (cudaMalloc does not guarantee zeroed memory)
167+
(backend.cudaMemset)(x_table_device.cast(), 0, x_table_size);
168+
(backend.cudaMemset)(z_table_device.cast(), 0, z_table_size);
169+
(backend.cudaMemset)(m_table_device.cast(), 0, m_table_size);
165170
}
166171

167172
Ok(Self {

crates/pecos-gpu-sims/src/gpu_stab.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,23 @@ const GATE_SWAP: u32 = 8;
2929

3030
/// Pack a single-qubit gate into the queue format
3131
fn pack_single_gate(gate_type: u32, target: u32) -> u32 {
32+
debug_assert!(
33+
target <= 0x3FFF,
34+
"qubit index {target} exceeds 14-bit limit"
35+
);
3236
(gate_type & 0xF) | ((target & 0x3FFF) << 4)
3337
}
3438

3539
/// Pack a two-qubit gate into the queue format
3640
fn pack_two_qubit_gate(gate_type: u32, control: u32, target: u32) -> u32 {
41+
debug_assert!(
42+
control <= 0x3FFF,
43+
"control qubit index {control} exceeds 14-bit limit"
44+
);
45+
debug_assert!(
46+
target <= 0x3FFF,
47+
"target qubit index {target} exceeds 14-bit limit"
48+
);
3749
(gate_type & 0xF) | ((target & 0x3FFF) << 4) | ((control & 0x3FFF) << 18)
3850
}
3951

@@ -303,6 +315,12 @@ impl<R: Rng + SeedableRng + Debug> GpuStab<R> {
303315
}))
304316
.map_err(|e| format!("Failed to create device: {e}"))?;
305317

318+
if num_qubits > 0x3FFF {
319+
return Err(format!(
320+
"GpuStab supports at most {} qubits, got {num_qubits}",
321+
0x3FFF
322+
));
323+
}
306324
let num_qubits = num_qubits as u32;
307325
let gen_words = num_qubits.div_ceil(32);
308326

0 commit comments

Comments
 (0)