use crate::constraints::EvalMode;
use crate::context::PackContext;
use crate::gencan::GencanWorkspace;
use crate::initial::restmol;
use crate::random::uniform01;
use molrs::types::F;
use rand::Rng;
pub struct MoveBadConfig<'a> {
pub movefrac: F,
pub maxmove_per_type: &'a [usize],
pub movebadrandom: bool,
pub gencan_maxit: usize,
}
pub fn movebad(
x: &mut [F],
sys: &mut PackContext,
precision: F,
cfg: &MoveBadConfig<'_>,
rng: &mut impl Rng,
workspace: &mut GencanWorkspace,
) {
let ntotat = sys.ntotat;
for icart in 0..ntotat {
sys.fdist_atom[icart] = 0.0;
sys.frest_atom[icart] = 0.0;
}
sys.move_flag = true;
sys.work.radiuswork.copy_from_slice(&sys.radius);
for i in 0..sys.ntotat {
sys.set_radius(i, sys.radius_ini[i]);
}
sys.evaluate(x, EvalMode::FOnly, None);
sys.move_flag = false;
let ntotmol = sys.ntotmol;
let mut icart_offset = 0usize;
for itype in 0..sys.ntype {
if !sys.comptype[itype] {
icart_offset += sys.nmols[itype] * sys.natoms[itype];
continue;
}
let nmols_itype = sys.nmols[itype];
let natoms_itype = sys.natoms[itype];
sys.work.fmol.clear();
sys.work.fmol.resize(nmols_itype, 0.0);
let fmol = &mut sys.work.fmol;
let mut nbad = 0usize;
let mut icart = icart_offset;
for score in fmol.iter_mut().take(nmols_itype) {
let mut fdist_mol = 0.0 as F;
let mut frest_mol = 0.0 as F;
for _ in 0..natoms_itype {
fdist_mol = fdist_mol.max(sys.fdist_atom[icart]);
frest_mol = frest_mol.max(sys.frest_atom[icart]);
icart += 1;
}
if fdist_mol > precision || frest_mol > precision {
nbad += 1;
*score = fdist_mol + frest_mol;
}
}
if nbad == 0 {
icart_offset += nmols_itype * natoms_itype;
continue;
}
let frac = (nbad as F / nmols_itype as F).min(cfg.movefrac);
let nmove_base = (nmols_itype as F * frac) as isize;
let nmove = usize::min(
cfg.maxmove_per_type[itype],
isize::max(nmove_base, 1) as usize,
);
let mflash = 1 + nmols_itype / 10;
sys.work.flash_ind.clear();
sys.work.flash_ind.extend(0..nmols_itype);
flash1(fmol, mflash, &mut sys.work.flash_ind, &mut sys.work.flash_l);
let mol_base: usize = {
let mut base = 0usize;
for it in 0..itype {
if sys.comptype[it] {
base += sys.nmols[it];
}
}
base
};
let move_pairs: Vec<(usize, usize)> = (0..nmove)
.map(|k| {
let ibad_mol = sys.work.flash_ind[nmols_itype - 1 - k];
let igood_mol = sys.work.flash_ind
[(uniform01(rng) * nmols_itype as F * frac) as usize % nmols_itype.max(1)];
(ibad_mol, igood_mol)
})
.collect();
for &(ibad_mol, igood_mol) in &move_pairs {
let ilubar_bad = (mol_base + ibad_mol) * 3;
let ilugan_bad = ntotmol * 3 + (mol_base + ibad_mol) * 3;
let ilubar_good = (mol_base + igood_mol) * 3;
let ilugan_good = ntotmol * 3 + (mol_base + igood_mol) * 3;
let dmax = sys.dmax[itype];
if cfg.movebadrandom {
x[ilubar_bad] = sys.sizemin[0] + uniform01(rng) * (sys.sizemax[0] - sys.sizemin[0]);
x[ilubar_bad + 1] =
sys.sizemin[1] + uniform01(rng) * (sys.sizemax[1] - sys.sizemin[1]);
x[ilubar_bad + 2] =
sys.sizemin[2] + uniform01(rng) * (sys.sizemax[2] - sys.sizemin[2]);
} else {
x[ilubar_bad] = x[ilubar_good] - 0.3 * dmax + 0.6 * uniform01(rng) * dmax;
x[ilubar_bad + 1] = x[ilubar_good + 1] - 0.3 * dmax + 0.6 * uniform01(rng) * dmax;
x[ilubar_bad + 2] = x[ilubar_good + 2] - 0.3 * dmax + 0.6 * uniform01(rng) * dmax;
}
x[ilugan_bad] = x[ilugan_good];
x[ilugan_bad + 1] = x[ilugan_good + 1];
x[ilugan_bad + 2] = x[ilugan_good + 2];
restmol(
itype,
ilubar_bad,
x,
sys,
precision,
cfg.gencan_maxit,
true,
workspace,
);
}
icart_offset += nmols_itype * natoms_itype;
}
sys.evaluate(x, EvalMode::FOnly, None);
for i in 0..sys.ntotat {
sys.set_radius(i, sys.work.radiuswork[i]);
}
}
pub fn flash1(a: &mut [F], m: usize, ind: &mut [usize], l_buf: &mut Vec<usize>) {
let n = a.len();
if n <= 1 {
return;
}
debug_assert_eq!(ind.len(), n);
let anmin = a.iter().copied().fold(F::INFINITY, F::min);
let nmax_idx = a
.iter()
.enumerate()
.max_by(|(_, x), (_, y)| x.total_cmp(y))
.map(|(i, _)| i)
.unwrap_or(0);
if a[0] == a[nmax_idx] {
return; }
let c1 = (m as F - 1.0) / (a[nmax_idx] - anmin);
l_buf.clear();
l_buf.resize(m, 0);
let l = l_buf;
for ai in a.iter().take(n) {
let k = (c1 * (*ai - anmin)) as usize;
let k = k.min(m - 1);
l[k] += 1;
}
for k in 1..m {
l[k] += l[k - 1];
}
a.swap(nmax_idx, 0);
ind.swap(nmax_idx, 0);
let mut nmove = 0usize;
let mut j = 0usize;
let mut k = m;
while nmove < n - 1 {
while j >= l[k - 1] {
j += 1;
if j >= n {
break;
}
k = (c1 * (a[j] - anmin)) as usize;
k = k.min(m - 1) + 1;
}
if j >= n {
break;
}
let mut flash = a[j];
let mut iflash = ind[j];
while j != l[k - 1] {
k = (c1 * (flash - anmin)) as usize;
k = k.min(m - 1);
let lk = l[k] - 1;
l[k] -= 1;
let hold = a[lk];
let ihold = ind[lk];
a[lk] = flash;
ind[lk] = iflash;
flash = hold;
iflash = ihold;
nmove += 1;
k = (c1 * (flash - anmin)) as usize;
k = k.min(m - 1) + 1;
}
}
let mut i = n as isize - 2;
while i >= 1 {
let iu = i as usize;
if a[iu + 1] < a[iu] {
let hold = a[iu];
let ihold = ind[iu];
let mut j = iu;
while j + 1 < n && a[j + 1] < hold {
a[j] = a[j + 1];
ind[j] = ind[j + 1];
j += 1;
}
a[j] = hold;
ind[j] = ihold;
}
i -= 1;
}
}