std::vector<unsigned int> some_random_configuration (unsigned int N_filled, const Lattice &lattice, RandomNumberGenerator &rng) { BOOST_ASSERT(N_filled <= lattice.total_sites()); const unsigned int n_dimensions = lattice.n_dimensions(); // If in more than one dimension, occasionally we want to try placing // particles such that they are distributed as well as possible over a // certain dimension (rungs, legs, etc). For determinantal wavefunctions // with certain orbital configurations, this can often be necessary to find // a non-zero amplitude in a reasonable amount of time. // // This method could be further optimized, but it is fast enough for now // (and is unlikely to be the bottleneck anyway). if (n_dimensions > 1 && rng.random_small_uint(3) == 0) { std::vector<unsigned int> v; std::set<unsigned int> vs; unsigned int spread_dimension = rng.random_small_uint(n_dimensions); unsigned int remaining = N_filled; while (remaining != 0) { std::vector<unsigned int> spread_coordinate; random_combination(spread_coordinate, std::min(remaining, (unsigned int) lattice.dimensions[spread_dimension]), lattice.dimensions[spread_dimension], rng); for (unsigned int i = 0; i < spread_coordinate.size(); ++i) { unsigned int proposed_site_index; do { LatticeSite proposed_site(lattice.n_dimensions()); proposed_site[spread_dimension] = spread_coordinate[i]; for (unsigned int j = 0; j < n_dimensions; ++j) { if (j != spread_dimension) proposed_site[j] = rng.random_small_uint(lattice.dimensions[j]); } proposed_site.basis_index = rng.random_small_uint(lattice.basis_indices); proposed_site_index = lattice.site_to_index(proposed_site); } while (!vs.insert(proposed_site_index).second); // try again until successful v.push_back(proposed_site_index); } remaining -= spread_coordinate.size(); } return v; } // otherwise, fall back to just blindly choosing a random combination of sites std::vector<unsigned int> v; random_combination(v, N_filled, lattice.total_sites(), rng); return v; }
// http://stackoverflow.com/questions/2394246/algorithm-to-select-a-single-random-combination-of-values void random_combination (std::vector<unsigned int> &v, unsigned int r, unsigned int n, RandomNumberGenerator &rng, unsigned int keep) { // per Jon Bentley's article in CACM, September 1987, Volume 30, Number 9 BOOST_ASSERT(n > 0); BOOST_ASSERT(r > 0); BOOST_ASSERT(r <= n); BOOST_ASSERT(keep <= n); BOOST_ASSERT(v.size() >= keep); if (n == r && keep == 0) { // the loop below fails if k == 0 is ever true, so here we handle the // only special case that could cause that v.resize(r); for (unsigned int i = 0; i < r; ++i) v[i] = i; return; } std::set<int> vs; v.resize(keep); v.reserve(r); for (std::vector<unsigned int>::const_iterator i = v.begin(); i != v.end(); ++i) vs.insert(*i); BOOST_ASSERT(v.size() == vs.size()); for (unsigned int k = n - r + keep; k < n; ++k) { BOOST_ASSERT(k > 0); unsigned int x = rng.random_small_uint(k); unsigned int a = (vs.find(x) != vs.end()) ? k : x; v.push_back(a); vs.insert(a); } BOOST_ASSERT(v.size() == r); }
probability_t BaseSwapPossibleWalk::compute_probability_ratio_of_random_transition (RandomNumberGenerator &rng) { BOOST_ASSERT(!transition_in_progress); transition_in_progress = true; const Lattice &lattice = phialpha1->get_lattice(); const Subsystem &subsystem = swapped_system->get_subsystem(); // decide which (zero-indexed) copy of the system to base this move around. // We call the chosen copy "copy A." const unsigned int copy_A = rng.random_small_uint(2); const PositionArguments &r_A = (copy_A == 0) ? phialpha1->get_positions() : phialpha2->get_positions(); const PositionArguments &r_B = (copy_A == 0) ? phialpha2->get_positions() : phialpha1->get_positions(); // first choose a random move in copy A chosen_particle_A = choose_random_particle(r_A, rng); const unsigned int particle_A_destination = plan_particle_move_to_nearby_empty_site(chosen_particle_A, r_A, lattice, rng); if (r_A[chosen_particle_A] == particle_A_destination) { autoreject_in_progress = true; return 0; } // If the particle we are moving in copy A is not going to change its // subsystem status, then we go ahead and make that move. If, however, the // particle we are moving in copy A changes its subsystem status (i.e., // enters or leaves the subsystem), then we must also choose a move in copy // B that does likewise, and take into account the transition probability // ratio so that the simulation maintains balance. unsigned int particle_B_destination = -1; // uninitialized real_t transition_ratio(1); const int copy_A_subsystem_particle_change = calculate_subsystem_particle_change(swapped_system->get_subsystem(), r_A[chosen_particle_A], particle_A_destination, lattice); const unsigned int species = chosen_particle_A.species; if (copy_A_subsystem_particle_change != 0) { const bool candidate_particle_B_subsystem_status = (copy_A_subsystem_particle_change == -1); // determine reverse/forward transition attempt probability ratios const unsigned int N_sites = r_A.get_N_sites(); const unsigned int N_filled = r_A.get_N_filled(species); const unsigned int N_within_subsystem = swapped_system->get_N_subsystem(species); const unsigned int N_outside_subsystem = N_filled - N_within_subsystem; const unsigned int N_vacant_within_subsystem = N_subsystem_sites - N_within_subsystem; const unsigned int N_vacant_outside_subsystem = (N_sites - N_filled) - N_vacant_within_subsystem; unsigned int forward_particle_possibilities, forward_vacant_possibilities, reverse_particle_possibilities, reverse_vacant_possibilities; if (copy_A_subsystem_particle_change == 1) { forward_particle_possibilities = N_outside_subsystem; forward_vacant_possibilities = N_vacant_within_subsystem; reverse_particle_possibilities = N_within_subsystem + 1; reverse_vacant_possibilities = N_vacant_outside_subsystem + 1; } else { BOOST_ASSERT(copy_A_subsystem_particle_change == -1); forward_particle_possibilities = N_within_subsystem; forward_vacant_possibilities = N_vacant_outside_subsystem; reverse_particle_possibilities = N_outside_subsystem + 1; reverse_vacant_possibilities = N_vacant_within_subsystem + 1; } if (forward_particle_possibilities == 0 || forward_vacant_possibilities == 0) { autoreject_in_progress = true; return 0; } transition_ratio = real_t(forward_particle_possibilities * forward_vacant_possibilities) / real_t(reverse_particle_possibilities * reverse_vacant_possibilities); // choose a particle from B with the same subsystem status as the // particle we are moving in A std::vector<unsigned int> candidate_particle_B_array; candidate_particle_B_array.reserve(forward_particle_possibilities); for (unsigned int i = 0; i < N_filled; ++i) { if (subsystem.position_is_within(r_B[Particle(i, species)], lattice) == candidate_particle_B_subsystem_status) candidate_particle_B_array.push_back(i); } BOOST_ASSERT(forward_particle_possibilities == candidate_particle_B_array.size()); chosen_particle_B = Particle(candidate_particle_B_array[rng.random_small_uint(candidate_particle_B_array.size())], species); // choose a destination such that the particle in copy B will change its // subsystem status const bool destination_B_in_subsystem = !candidate_particle_B_subsystem_status; std::vector<unsigned int> candidate_destination_B_array; candidate_destination_B_array.reserve(forward_vacant_possibilities); for (unsigned int i = 0; i < N_sites; ++i) { if (!r_B.is_occupied(i, species) && subsystem.position_is_within(i, lattice) == destination_B_in_subsystem) candidate_destination_B_array.push_back(i); } BOOST_ASSERT(forward_vacant_possibilities == candidate_destination_B_array.size()); particle_B_destination = candidate_destination_B_array[rng.random_small_uint(candidate_destination_B_array.size())]; } // translate from "copy A or B" language back to "copy 1 or 2" const Particle * const chosen_particle_B_ptr = (copy_A_subsystem_particle_change != 0) ? &chosen_particle_B : 0; chosen_particle1 = (copy_A == 0) ? &chosen_particle_A : chosen_particle_B_ptr; chosen_particle2 = (copy_A == 0) ? chosen_particle_B_ptr : &chosen_particle_A; // move particles, determining phialpha probability ratios amplitude_t phialpha1_ratio(1), phialpha2_ratio(1); if (chosen_particle1) { const Big<amplitude_t> old_phialpha1_psi(phialpha1->psi()); if (!phialpha1.unique()) // copy-on-write phialpha1 = phialpha1->clone(); Move move; move.push_back(SingleParticleMove(*chosen_particle1, (copy_A == 0) ? particle_A_destination : particle_B_destination)); phialpha1->perform_move(move); phialpha1_ratio = phialpha1->psi().ratio(old_phialpha1_psi); } if (chosen_particle2) { const Big<amplitude_t> old_phialpha2_psi(phialpha2->psi()); if (!phialpha2.unique()) // copy-on-write phialpha2 = phialpha2->clone(); Move move; move.push_back(SingleParticleMove(*chosen_particle2, (copy_A == 0) ? particle_B_destination : particle_A_destination)); phialpha2->perform_move(move); phialpha2_ratio = phialpha2->psi().ratio(old_phialpha2_psi); } amplitude_t phibeta1_ratio(0), phibeta2_ratio(0); if (update_swapped_system_before_accepting) { // remember old phibeta's const Big<amplitude_t> old_phibeta1_psi(swapped_system->get_phibeta1().psi()); const Big<amplitude_t> old_phibeta2_psi(swapped_system->get_phibeta2().psi()); // implement copy-on-write if (!swapped_system.unique()) swapped_system = boost::make_shared<SwappedSystem>(*swapped_system); // update phibeta's swapped_system->update(chosen_particle1, chosen_particle2, *phialpha1, *phialpha2); // determine probability ratios phibeta1_ratio = swapped_system->get_phibeta1().psi().ratio(old_phibeta1_psi); phibeta2_ratio = swapped_system->get_phibeta2().psi().ratio(old_phibeta2_psi); } // return a probability return transition_ratio * probability_ratio(phialpha1_ratio, phialpha2_ratio, phibeta1_ratio, phibeta2_ratio); }