use crate::{
error::{QuantRS2Error, QuantRS2Result},
qubit::QubitId,
};
use rustc_hash::FxHashMap;
use scirs2_core::ndarray::{Array1, Array2};
use scirs2_core::Complex64;
use std::collections::HashMap;
use std::f64::consts::PI;
#[derive(Debug, Clone, PartialEq)]
pub enum AtomSpecies {
Rb87,
Cs133,
Sr88,
Yb171,
Custom {
mass: f64, ground_state: String, rydberg_state: String, },
}
impl AtomSpecies {
pub const fn mass(&self) -> f64 {
match self {
Self::Rb87 => 86.909_183,
Self::Cs133 => 132.905_447,
Self::Sr88 => 87.905_614,
Self::Yb171 => 170.936_426,
Self::Custom { mass, .. } => *mass,
}
}
pub const fn typical_trap_depth(&self) -> f64 {
match self {
Self::Rb87 | Self::Custom { .. } => 1000.0,
Self::Cs133 => 800.0,
Self::Sr88 => 1200.0,
Self::Yb171 => 900.0,
}
}
pub const fn rydberg_energy(&self) -> f64 {
match self {
Self::Rb87 | Self::Custom { .. } => 100.0, Self::Cs133 => 95.0,
Self::Sr88 => 110.0,
Self::Yb171 => 105.0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Position3D {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl Position3D {
pub const fn new(x: f64, y: f64, z: f64) -> Self {
Self { x, y, z }
}
pub fn distance_to(&self, other: &Self) -> f64 {
(self.z - other.z)
.mul_add(
self.z - other.z,
(self.y - other.y).mul_add(self.y - other.y, (self.x - other.x).powi(2)),
)
.sqrt()
}
pub fn rydberg_interaction(&self, other: &Self, c6: f64) -> f64 {
let distance = self.distance_to(other);
if distance == 0.0 {
f64::INFINITY
} else {
c6 / distance.powi(6) }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AtomState {
Ground,
Rydberg,
Intermediate,
Missing,
}
#[derive(Debug, Clone)]
pub struct NeutralAtom {
pub species: AtomSpecies,
pub position: Position3D,
pub state: AtomState,
pub loading_probability: f64,
pub lifetime: f64,
pub coherence_time: f64,
}
impl NeutralAtom {
pub const fn new(species: AtomSpecies, position: Position3D) -> Self {
Self {
species,
position,
state: AtomState::Ground,
loading_probability: 0.9, lifetime: 100.0, coherence_time: 1.0, }
}
pub fn is_loaded(&self) -> bool {
self.state != AtomState::Missing
}
pub fn interaction_with(&self, other: &Self) -> f64 {
let c6 = match (&self.species, &other.species) {
(AtomSpecies::Cs133, AtomSpecies::Cs133) => 6000.0,
(AtomSpecies::Sr88, AtomSpecies::Sr88) => 4500.0,
(AtomSpecies::Yb171, AtomSpecies::Yb171) => 4800.0,
(AtomSpecies::Rb87, AtomSpecies::Rb87) | _ => 5000.0, };
self.position.rydberg_interaction(&other.position, c6)
}
}
#[derive(Debug, Clone)]
pub struct OpticalTweezer {
pub position: Position3D,
pub power: f64,
pub wavelength: f64,
pub numerical_aperture: f64,
pub active: bool,
pub atom: Option<NeutralAtom>,
}
impl OpticalTweezer {
pub const fn new(
position: Position3D,
power: f64,
wavelength: f64,
numerical_aperture: f64,
) -> Self {
Self {
position,
power,
wavelength,
numerical_aperture,
active: true,
atom: None,
}
}
pub fn trap_depth(&self) -> f64 {
if !self.active {
return 0.0;
}
let beam_waist = self.wavelength / (PI * self.numerical_aperture); (self.power * 1000.0) / (self.wavelength * beam_waist.powi(2))
}
pub fn load_atom(&mut self, mut atom: NeutralAtom) -> bool {
if !self.active || self.atom.is_some() {
return false;
}
use scirs2_core::random::prelude::*;
let mut rng = thread_rng();
let success = rng.random::<f64>() < atom.loading_probability;
if success {
atom.position = self.position;
self.atom = Some(atom);
true
} else {
false
}
}
pub const fn remove_atom(&mut self) -> Option<NeutralAtom> {
self.atom.take()
}
pub const fn has_atom(&self) -> bool {
self.atom.is_some()
}
}
#[derive(Debug, Clone)]
pub struct LaserSystem {
pub wavelength: f64,
pub power: f64,
pub beam_waist: f64,
pub detuning: f64,
pub linewidth: f64,
pub active: bool,
}
impl LaserSystem {
pub const fn new(wavelength: f64, power: f64, beam_waist: f64, detuning: f64) -> Self {
Self {
wavelength,
power,
beam_waist,
detuning,
linewidth: 1.0, active: false,
}
}
pub fn rabi_frequency(&self) -> f64 {
if !self.active {
return 0.0;
}
(self.power.sqrt() * 10.0) / self.beam_waist
}
pub const fn set_detuning(&mut self, detuning: f64) {
self.detuning = detuning;
}
pub const fn set_active(&mut self, active: bool) {
self.active = active;
}
}
#[derive(Debug)]
pub struct NeutralAtomQC {
pub tweezers: Vec<OpticalTweezer>,
pub qubit_map: FxHashMap<QubitId, usize>,
pub lasers: HashMap<String, LaserSystem>,
pub state: Array1<Complex64>,
pub num_qubits: usize,
pub global_phase: f64,
pub error_model: NeutralAtomErrorModel,
}
impl NeutralAtomQC {
pub fn new(num_qubits: usize) -> Self {
let mut tweezers = Vec::new();
let mut qubit_map = FxHashMap::default();
for i in 0..num_qubits {
let position = Position3D::new(i as f64 * 5.0, 0.0, 0.0); let tweezer = OpticalTweezer::new(
position, 1.0, 1064.0, 0.75, );
tweezers.push(tweezer);
qubit_map.insert(QubitId(i as u32), i);
}
let mut lasers = HashMap::new();
lasers.insert(
"cooling".to_string(),
LaserSystem::new(780.0, 10.0, 100.0, -10.0),
);
lasers.insert(
"rydberg".to_string(),
LaserSystem::new(480.0, 1.0, 2.0, 0.0),
);
lasers.insert("raman".to_string(), LaserSystem::new(795.0, 5.0, 10.0, 0.0));
let dim = 1 << num_qubits;
let mut state = Array1::zeros(dim);
state[0] = Complex64::new(1.0, 0.0);
Self {
tweezers,
qubit_map,
lasers,
state,
num_qubits,
global_phase: 0.0,
error_model: NeutralAtomErrorModel::default(),
}
}
pub fn load_atoms(&mut self, species: AtomSpecies) -> QuantRS2Result<usize> {
let mut loaded_count = 0;
for tweezer in &mut self.tweezers {
let atom = NeutralAtom::new(species.clone(), tweezer.position);
if tweezer.load_atom(atom) {
loaded_count += 1;
}
}
Ok(loaded_count)
}
pub fn rearrange_atoms(&mut self) -> QuantRS2Result<()> {
let mut atoms = Vec::new();
for tweezer in &mut self.tweezers {
if let Some(atom) = tweezer.remove_atom() {
atoms.push(atom);
}
}
for (i, atom) in atoms.into_iter().enumerate() {
if i < self.tweezers.len() {
self.tweezers[i].load_atom(atom);
}
}
Ok(())
}
pub fn apply_single_qubit_gate(
&mut self,
qubit: QubitId,
gate_matrix: &Array2<Complex64>,
) -> QuantRS2Result<()> {
let tweezer_idx = *self
.qubit_map
.get(&qubit)
.ok_or_else(|| QuantRS2Error::InvalidInput(format!("Qubit {qubit:?} not found")))?;
if !self.tweezers[tweezer_idx].has_atom() {
return Err(QuantRS2Error::InvalidOperation(
"No atom in tweezer".to_string(),
));
}
let qubit_index = qubit.0 as usize;
let new_state = self.apply_single_qubit_tensor(qubit_index, gate_matrix)?;
self.state = new_state;
self.apply_single_qubit_errors(qubit_index)?;
Ok(())
}
pub fn apply_rydberg_gate(&mut self, control: QubitId, target: QubitId) -> QuantRS2Result<()> {
let control_idx = *self.qubit_map.get(&control).ok_or_else(|| {
QuantRS2Error::InvalidInput(format!("Control qubit {control:?} not found"))
})?;
let target_idx = *self.qubit_map.get(&target).ok_or_else(|| {
QuantRS2Error::InvalidInput(format!("Target qubit {target:?} not found"))
})?;
if !self.tweezers[control_idx].has_atom() || !self.tweezers[target_idx].has_atom() {
return Err(QuantRS2Error::InvalidOperation(
"Missing atoms for two-qubit gate".to_string(),
));
}
let control_atom = self.tweezers[control_idx].atom.as_ref().ok_or_else(|| {
QuantRS2Error::InvalidOperation("Control atom unexpectedly missing".to_string())
})?;
let target_atom = self.tweezers[target_idx].atom.as_ref().ok_or_else(|| {
QuantRS2Error::InvalidOperation("Target atom unexpectedly missing".to_string())
})?;
let interaction = control_atom.interaction_with(target_atom);
if interaction < 1.0 {
return Err(QuantRS2Error::InvalidOperation(
"Insufficient Rydberg interaction".to_string(),
));
}
let cz_matrix = self.create_rydberg_cz_matrix()?;
let new_state =
self.apply_two_qubit_tensor(control.0 as usize, target.0 as usize, &cz_matrix)?;
self.state = new_state;
self.apply_two_qubit_errors(control.0 as usize, target.0 as usize)?;
Ok(())
}
fn create_rydberg_cz_matrix(&self) -> QuantRS2Result<Array2<Complex64>> {
let mut cz = Array2::eye(4);
cz[[3, 3]] = Complex64::new(-1.0, 0.0); Ok(cz)
}
fn apply_single_qubit_tensor(
&self,
qubit_idx: usize,
gate: &Array2<Complex64>,
) -> QuantRS2Result<Array1<Complex64>> {
let dim = 1 << self.num_qubits;
let mut new_state = Array1::zeros(dim);
for state in 0..dim {
let qubit_bit = (state >> qubit_idx) & 1;
let other_bits = state & !(1 << qubit_idx);
for new_qubit_bit in 0..2 {
let new_state_idx = other_bits | (new_qubit_bit << qubit_idx);
let gate_element = gate[[new_qubit_bit, qubit_bit]];
new_state[new_state_idx] += gate_element * self.state[state];
}
}
Ok(new_state)
}
fn apply_two_qubit_tensor(
&self,
qubit1: usize,
qubit2: usize,
gate: &Array2<Complex64>,
) -> QuantRS2Result<Array1<Complex64>> {
let dim = 1 << self.num_qubits;
let mut new_state = Array1::zeros(dim);
for state in 0..dim {
let bit1 = (state >> qubit1) & 1;
let bit2 = (state >> qubit2) & 1;
let other_bits = state & !((1 << qubit1) | (1 << qubit2));
let two_qubit_state = (bit1 << 1) | bit2;
for new_two_qubit_state in 0..4 {
let new_bit1 = (new_two_qubit_state >> 1) & 1;
let new_bit2 = new_two_qubit_state & 1;
let new_state_idx = other_bits | (new_bit1 << qubit1) | (new_bit2 << qubit2);
let gate_element = gate[[new_two_qubit_state, two_qubit_state]];
new_state[new_state_idx] += gate_element * self.state[state];
}
}
Ok(new_state)
}
fn apply_single_qubit_errors(&mut self, _qubit: usize) -> QuantRS2Result<()> {
use scirs2_core::random::prelude::*;
let mut rng = thread_rng();
let phase_error = rng.random_range(-0.01..0.01);
self.global_phase += phase_error;
Ok(())
}
fn apply_two_qubit_errors(&mut self, _qubit1: usize, _qubit2: usize) -> QuantRS2Result<()> {
use scirs2_core::random::prelude::*;
let mut rng = thread_rng();
let phase_error = rng.random_range(-0.05..0.05);
self.global_phase += phase_error;
Ok(())
}
pub fn measure_qubit(&mut self, qubit: QubitId) -> QuantRS2Result<u8> {
let qubit_idx = qubit.0 as usize;
if qubit_idx >= self.num_qubits {
return Err(QuantRS2Error::InvalidInput(
"Qubit index out of range".to_string(),
));
}
let mut prob_0 = 0.0;
let mut prob_1 = 0.0;
for state in 0..(1 << self.num_qubits) {
let amplitude_sq = self.state[state].norm_sqr();
if (state >> qubit_idx) & 1 == 0 {
prob_0 += amplitude_sq;
} else {
prob_1 += amplitude_sq;
}
}
use scirs2_core::random::prelude::*;
let mut rng = thread_rng();
let result: usize = usize::from(rng.random::<f64>() >= prob_0 / (prob_0 + prob_1));
let mut new_state = Array1::zeros(1 << self.num_qubits);
let normalization = if result == 0 {
prob_0.sqrt()
} else {
prob_1.sqrt()
};
for state in 0..(1 << self.num_qubits) {
if ((state >> qubit_idx) & 1) == result {
new_state[state] = self.state[state] / normalization;
}
}
self.state = new_state;
Ok(result as u8)
}
pub fn loaded_atom_count(&self) -> usize {
self.tweezers.iter().filter(|t| t.has_atom()).count()
}
pub fn get_probabilities(&self) -> Vec<f64> {
self.state.iter().map(|c| c.norm_sqr()).collect()
}
pub fn reset(&mut self) {
let dim = 1 << self.num_qubits;
self.state = Array1::zeros(dim);
self.state[0] = Complex64::new(1.0, 0.0);
self.global_phase = 0.0;
}
pub fn get_atom_positions(&self) -> Vec<(QubitId, Position3D, bool)> {
self.tweezers
.iter()
.enumerate()
.filter_map(|(i, tweezer)| {
self.qubit_map
.iter()
.find(|(_, &idx)| idx == i)
.map(|(&qubit_id, _)| (qubit_id, tweezer.position, tweezer.has_atom()))
})
.collect()
}
}
#[derive(Debug, Clone)]
pub struct NeutralAtomErrorModel {
pub loading_fidelity: f64,
pub single_qubit_fidelity: f64,
pub two_qubit_fidelity: f64,
pub measurement_fidelity: f64,
pub coherence_time: f64,
pub blockade_radius: f64,
}
impl Default for NeutralAtomErrorModel {
fn default() -> Self {
Self {
loading_fidelity: 0.95,
single_qubit_fidelity: 0.999,
two_qubit_fidelity: 0.98,
measurement_fidelity: 0.99,
coherence_time: 1.0,
blockade_radius: 10.0,
}
}
}
pub struct NeutralAtomGates;
impl NeutralAtomGates {
pub fn x_gate() -> Array2<Complex64> {
let mut x = Array2::zeros((2, 2));
x[[0, 1]] = Complex64::new(1.0, 0.0);
x[[1, 0]] = Complex64::new(1.0, 0.0);
x
}
pub fn y_gate() -> Array2<Complex64> {
let mut y = Array2::zeros((2, 2));
y[[0, 1]] = Complex64::new(0.0, -1.0);
y[[1, 0]] = Complex64::new(0.0, 1.0);
y
}
pub fn z_gate() -> Array2<Complex64> {
let mut z = Array2::zeros((2, 2));
z[[0, 0]] = Complex64::new(1.0, 0.0);
z[[1, 1]] = Complex64::new(-1.0, 0.0);
z
}
pub fn h_gate() -> Array2<Complex64> {
let h_val = 1.0 / 2.0_f64.sqrt();
let mut h = Array2::zeros((2, 2));
h[[0, 0]] = Complex64::new(h_val, 0.0);
h[[0, 1]] = Complex64::new(h_val, 0.0);
h[[1, 0]] = Complex64::new(h_val, 0.0);
h[[1, 1]] = Complex64::new(-h_val, 0.0);
h
}
pub fn rx_gate(theta: f64) -> Array2<Complex64> {
let cos_half = (theta / 2.0).cos();
let sin_half = (theta / 2.0).sin();
let mut rx = Array2::zeros((2, 2));
rx[[0, 0]] = Complex64::new(cos_half, 0.0);
rx[[0, 1]] = Complex64::new(0.0, -sin_half);
rx[[1, 0]] = Complex64::new(0.0, -sin_half);
rx[[1, 1]] = Complex64::new(cos_half, 0.0);
rx
}
pub fn ry_gate(theta: f64) -> Array2<Complex64> {
let cos_half = (theta / 2.0).cos();
let sin_half = (theta / 2.0).sin();
let mut ry = Array2::zeros((2, 2));
ry[[0, 0]] = Complex64::new(cos_half, 0.0);
ry[[0, 1]] = Complex64::new(-sin_half, 0.0);
ry[[1, 0]] = Complex64::new(sin_half, 0.0);
ry[[1, 1]] = Complex64::new(cos_half, 0.0);
ry
}
pub fn rz_gate(theta: f64) -> Array2<Complex64> {
let exp_neg = Complex64::new(0.0, -theta / 2.0).exp();
let exp_pos = Complex64::new(0.0, theta / 2.0).exp();
let mut rz = Array2::zeros((2, 2));
rz[[0, 0]] = exp_neg;
rz[[1, 1]] = exp_pos;
rz
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_atom_species_properties() {
let rb87 = AtomSpecies::Rb87;
assert!((rb87.mass() - 86.909183).abs() < 1e-6);
assert!(rb87.typical_trap_depth() > 0.0);
assert!(rb87.rydberg_energy() > 0.0);
let custom = AtomSpecies::Custom {
mass: 100.0,
ground_state: "5s".to_string(),
rydberg_state: "50s".to_string(),
};
assert_eq!(custom.mass(), 100.0);
}
#[test]
fn test_position_calculations() {
let pos1 = Position3D::new(0.0, 0.0, 0.0);
let pos2 = Position3D::new(3.0, 4.0, 0.0);
assert_eq!(pos1.distance_to(&pos2), 5.0);
let interaction = pos1.rydberg_interaction(&pos2, 1000.0);
assert!(interaction > 0.0);
assert!(interaction.is_finite());
}
#[test]
fn test_optical_tweezer() {
let position = Position3D::new(0.0, 0.0, 0.0);
let mut tweezer = OpticalTweezer::new(position, 1.0, 1064.0, 0.75);
assert!(tweezer.active);
assert!(!tweezer.has_atom());
assert!(tweezer.trap_depth() > 0.0);
let atom = NeutralAtom::new(AtomSpecies::Rb87, position);
let loaded = tweezer.load_atom(atom);
let _result = loaded; }
#[test]
fn test_laser_system() {
let mut laser = LaserSystem::new(780.0, 10.0, 100.0, -10.0);
assert!(!laser.active);
assert_eq!(laser.rabi_frequency(), 0.0);
laser.set_active(true);
assert!(laser.rabi_frequency() > 0.0);
laser.set_detuning(5.0);
assert_eq!(laser.detuning, 5.0);
}
#[test]
fn test_neutral_atom_creation() {
let position = Position3D::new(1.0, 2.0, 3.0);
let atom = NeutralAtom::new(AtomSpecies::Rb87, position);
assert_eq!(atom.species, AtomSpecies::Rb87);
assert_eq!(atom.position, position);
assert_eq!(atom.state, AtomState::Ground);
assert!(atom.is_loaded());
}
#[test]
fn test_neutral_atom_qc_initialization() {
let qc = NeutralAtomQC::new(3);
assert_eq!(qc.num_qubits, 3);
assert_eq!(qc.tweezers.len(), 3);
assert_eq!(qc.qubit_map.len(), 3);
assert_eq!(qc.state.len(), 8);
assert!((qc.state[0].norm_sqr() - 1.0).abs() < 1e-10);
for i in 1..8 {
assert!(qc.state[i].norm_sqr() < 1e-10);
}
}
#[test]
fn test_atom_loading() {
let mut qc = NeutralAtomQC::new(2);
let loaded = qc
.load_atoms(AtomSpecies::Rb87)
.expect("Failed to load atoms");
assert!(loaded <= 2);
assert!(qc.loaded_atom_count() <= 2);
}
#[test]
fn test_single_qubit_gates() {
let mut qc = NeutralAtomQC::new(1);
qc.load_atoms(AtomSpecies::Rb87)
.expect("Failed to load atoms");
if qc.loaded_atom_count() > 0 {
let x_gate = NeutralAtomGates::x_gate();
let result = qc.apply_single_qubit_gate(QubitId(0), &x_gate);
assert!(result.is_ok());
}
}
#[test]
fn test_two_qubit_rydberg_gate() {
let mut qc = NeutralAtomQC::new(2);
qc.load_atoms(AtomSpecies::Rb87)
.expect("Failed to load atoms");
if qc.loaded_atom_count() == 2 {
let result = qc.apply_rydberg_gate(QubitId(0), QubitId(1));
assert!(result.is_ok() || result.is_err()); }
}
#[test]
fn test_measurement() {
let mut qc = NeutralAtomQC::new(1);
qc.load_atoms(AtomSpecies::Rb87)
.expect("Failed to load atoms");
if qc.loaded_atom_count() > 0 {
let result = qc.measure_qubit(QubitId(0));
if let Ok(measurement) = result {
assert!(measurement == 0 || measurement == 1);
}
}
}
#[test]
fn test_gate_matrices() {
let x = NeutralAtomGates::x_gate();
let y = NeutralAtomGates::y_gate();
let z = NeutralAtomGates::z_gate();
let h = NeutralAtomGates::h_gate();
assert_eq!(x.dim(), (2, 2));
assert_eq!(y.dim(), (2, 2));
assert_eq!(z.dim(), (2, 2));
assert_eq!(h.dim(), (2, 2));
assert_eq!(x[[0, 1]], Complex64::new(1.0, 0.0));
assert_eq!(x[[1, 0]], Complex64::new(1.0, 0.0));
assert_eq!(x[[0, 0]], Complex64::new(0.0, 0.0));
assert_eq!(x[[1, 1]], Complex64::new(0.0, 0.0));
}
#[test]
fn test_rotation_gates() {
let rx_pi = NeutralAtomGates::rx_gate(PI);
let _ry_pi = NeutralAtomGates::ry_gate(PI);
let _rz_pi = NeutralAtomGates::rz_gate(PI);
let x = NeutralAtomGates::x_gate();
let expected_rx = x.mapv(|x| Complex64::new(0.0, -1.0) * x);
for i in 0..2 {
for j in 0..2 {
assert!((rx_pi[[i, j]] - expected_rx[[i, j]]).norm() < 1e-10);
}
}
}
#[test]
fn test_atom_rearrangement() {
let mut qc = NeutralAtomQC::new(3);
let mut atom1 = NeutralAtom::new(AtomSpecies::Rb87, qc.tweezers[0].position);
let mut atom2 = NeutralAtom::new(AtomSpecies::Rb87, qc.tweezers[2].position);
atom1.loading_probability = 1.0; atom2.loading_probability = 1.0; qc.tweezers[0].atom = Some(atom1);
qc.tweezers[2].atom = Some(atom2);
assert_eq!(qc.loaded_atom_count(), 2);
assert!(qc.rearrange_atoms().is_ok());
assert!(qc.tweezers[0].has_atom());
assert!(qc.tweezers[1].has_atom());
assert!(!qc.tweezers[2].has_atom());
}
#[test]
fn test_error_model_defaults() {
let error_model = NeutralAtomErrorModel::default();
assert!(error_model.loading_fidelity > 0.0 && error_model.loading_fidelity <= 1.0);
assert!(
error_model.single_qubit_fidelity > 0.0 && error_model.single_qubit_fidelity <= 1.0
);
assert!(error_model.two_qubit_fidelity > 0.0 && error_model.two_qubit_fidelity <= 1.0);
assert!(error_model.measurement_fidelity > 0.0 && error_model.measurement_fidelity <= 1.0);
assert!(error_model.coherence_time > 0.0);
assert!(error_model.blockade_radius > 0.0);
}
#[test]
fn test_quantum_state_probabilities() {
let qc = NeutralAtomQC::new(2);
let probs = qc.get_probabilities();
assert_eq!(probs.len(), 4);
let total: f64 = probs.iter().sum();
assert!((total - 1.0).abs() < 1e-10);
assert!((probs[0] - 1.0).abs() < 1e-10);
}
#[test]
fn test_atom_position_retrieval() {
let qc = NeutralAtomQC::new(2);
let positions = qc.get_atom_positions();
assert_eq!(positions.len(), 2);
for (qubit_id, position, has_atom) in positions {
assert!(qubit_id.0 < 2);
assert!(position.x >= 0.0); let _ = has_atom;
}
}
}