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); }
AmplitudeType BasicOperatorEvaluator::evaluate (const typename Wavefunction<AmplitudeType>::Amplitude &wfa, const BoundaryConditions<AmplitudeType> &bcs) const { typedef AmplitudeType PhaseType; assert(&wfa.get_lattice() == m_operator.lattice.get()); assert(wfa.get_positions().get_N_species() >= min_required_species); const PositionArguments &r = wfa.get_positions(); const Lattice &lattice = wfa.get_lattice(); const bool is_sum_over_sites = (bcs.size() != 0); AmplitudeType meas = 0; const Big<AmplitudeType> old_psi(wfa.psi()); // we only iterate if doing a sum, and even then we only want to iterate // over BraivaisSite's const unsigned int n_iterations = is_sum_over_sites ? lattice.total_sites() / lattice.basis_indices : 1; // FIXME: need a faster way of iterating the lattice for (unsigned int i = 0; i < n_iterations; ++i) { const LatticeSite site_offset(lattice[i]); // we only want to iterate over BravaisSite's assert(site_offset.basis_index == 0); PhaseType phase = 1; Move move; for (unsigned int j = 0; j < m_operator.hopv.size(); ++j) { PhaseType srcphase; const unsigned int species = m_operator.hopv[j].get_species(); LatticeSite src(m_operator.hopv[j].get_source()); lattice.asm_add_site_vector(src, site_offset.bravais_site()); assert(is_sum_over_sites || lattice.site_is_valid(src)); srcphase = lattice.enforce_boundary(src, bcs); if (srcphase == PhaseType(0)) goto current_measurement_is_zero; const int particle_index = r.particle_index_at_position(lattice.index(src), species); if (particle_index < 0) goto current_measurement_is_zero; if (m_operator.hopv[j].get_source() != m_operator.hopv[j].get_destination()) { LatticeSite dest(m_operator.hopv[j].get_destination()); lattice.asm_add_site_vector(dest, site_offset.bravais_site()); assert(is_sum_over_sites || lattice.site_is_valid(dest)); phase *= lattice.enforce_boundary(dest, bcs) / srcphase; if (phase == PhaseType(0)) goto current_measurement_is_zero; const unsigned int dest_index = lattice.index(dest); if (r.is_occupied(dest_index, species)) goto current_measurement_is_zero; move.push_back(SingleParticleMove(Particle(particle_index, species), dest_index)); } } // now perform the move (if necessary) if (move.size() != 0) { BasicOperatorEvaluatorLocal::TemporaryMove<AmplitudeType> temp_move(wfa, move); // fixme: check logic of multiplying by phase (c.f. above), as // well as logic of source and destination meas += vmc_conj(phase * wfa.psi().ratio(old_psi)); } else { meas += 1; } current_measurement_is_zero: ; } return meas; }
void SwappedSystem::update (const Particle *particle1, const Particle *particle2, const Wavefunction<amplitude_t>::Amplitude &phialpha1, const Wavefunction<amplitude_t>::Amplitude &phialpha2) { // this function should be called *after* the phialpha's have been updated assert(current_state == READY); current_state = UPDATE_IN_PROGRESS; const PositionArguments &r1 = phialpha1.get_positions(); const PositionArguments &r2 = phialpha2.get_positions(); #ifndef NDEBUG assert(r1.get_N_species() == r2.get_N_species()); for (unsigned int i = 0; i < r1.get_N_species(); ++i) assert(r1.get_N_filled(i) == r2.get_N_filled(i)); #endif assert(!particle1 || r1.particle_is_valid(*particle1)); assert(!particle2 || r2.particle_is_valid(*particle2)); const Lattice &lattice = phialpha1.get_lattice(); // these will be will be >= 0 if the particle was in the subsystem before, // -1 if the particle was not in the subsystem before, and -2 if the // particle isn't even being moved. int pairing_index1 = (!particle1) ? -2 : vector_find(copy1_subsystem_indices[particle1->species], particle1->index); int pairing_index2 = (!particle2) ? -2 : vector_find(copy2_subsystem_indices[particle2->species], particle2->index); const bool particle1_now_in_subsystem = (particle1 && subsystem->position_is_within(r1[*particle1], lattice)); const bool particle2_now_in_subsystem = (particle2 && subsystem->position_is_within(r2[*particle2], lattice)); const int delta1 = (particle1_now_in_subsystem ? 1 : 0) + (pairing_index1 >= 0 ? -1 : 0); #ifndef NDEBUG const int delta2 = (particle2_now_in_subsystem ? 1 : 0) + (pairing_index2 >= 0 ? -1 : 0); #endif assert(particle1 || delta1 == 0); assert(particle2 || delta2 == 0); assert(delta1 == delta2); const int delta = delta1; assert(delta == 0 || (particle1 && particle2 && particle1->species == particle2->species)); assert(delta == 0 || particle1_now_in_subsystem == particle2_now_in_subsystem); // to ensure only a single update is necessary to the phibeta's, we require // that a particle only be moved in one copy if the particle number is not // changing assert(delta != 0 || !(particle1 && particle2)); // remember a few things in case we need to cancel recent_delta = delta; if (particle1) recent_particle1 = *particle1; if (particle2) recent_particle2 = *particle2; if (delta == -1) { // if a particle of the same type leaves each subsystem simultaneously, // we need to use some special logic in case we have to re-pair the // remaining particles in the subsystem. (re-pair in the sense of what // gets swapped with what) // these repeat some logic in the "if" statement, but are a useful // sanity check nonetheless (for now) assert(pairing_index1 >= 0 && pairing_index2 >= 0); assert(!particle1_now_in_subsystem); assert(!particle2_now_in_subsystem); const unsigned int species = particle1->species; std::vector<unsigned int> &c1_s = copy1_subsystem_indices[species]; std::vector<unsigned int> &c2_s = copy2_subsystem_indices[species]; if (pairing_index1 != pairing_index2) { // in order to re-pair the particles left behind, we move the pair // that is leaving the subsystem to the max_pairing_index, and move // the pair that is staying behind to the min_pairing_index. phibeta1->swap_particles(c1_s[pairing_index1], c1_s[pairing_index2], species); phibeta2->swap_particles(c2_s[pairing_index1], c2_s[pairing_index2], species); if (pairing_index1 < pairing_index2) std::swap(c1_s[pairing_index1], c1_s[pairing_index2]); else std::swap(c2_s[pairing_index1], c2_s[pairing_index2]); } // update the phibeta's const unsigned int max_pairing_index = std::max(pairing_index1, pairing_index2); assert(!phibeta1_dirty && !phibeta2_dirty); { Move move; move.push_back(SingleParticleMove(Particle(c1_s[max_pairing_index], species), r1[*particle1])); phibeta1->perform_move(move); } { Move move; move.push_back(SingleParticleMove(Particle(c2_s[max_pairing_index], species), r2[*particle2])); phibeta2->perform_move(move); } phibeta1_dirty = true; phibeta2_dirty = true; // remove the empty pair in the subsystem indices (yes, these steps // make sense whether we had to re-pair or not) c1_s[max_pairing_index] = c1_s[c1_s.size() - 1]; c1_s.pop_back(); c2_s[max_pairing_index] = c2_s[c2_s.size() - 1]; c2_s.pop_back(); } else { assert(delta == 0 || delta == 1); // either both particles moved within their respective subsystems // (if they moved at all), or both entered the subsystem and paired // with each other immediately // update the subsystem indices if necessary if (delta == 1) { std::vector<unsigned int> &c1_s = copy1_subsystem_indices[particle1->species]; std::vector<unsigned int> &c2_s = copy2_subsystem_indices[particle2->species]; c1_s.push_back(particle1->index); pairing_index1 = c1_s.size() - 1; c2_s.push_back(particle2->index); pairing_index2 = c2_s.size() - 1; } assert(subsystem_particle_counts_match()); // update the phibeta's if (particle1) { std::unique_ptr<Wavefunction<amplitude_t>::Amplitude> &phibeta = particle1_now_in_subsystem ? phibeta2 : phibeta1; bool &phibeta_dirty = particle1_now_in_subsystem ? phibeta2_dirty : phibeta1_dirty; const Particle phibeta_particle = particle1_now_in_subsystem ? Particle(copy2_subsystem_indices[particle1->species][pairing_index1], particle1->species) : *particle1; assert(!phibeta_dirty); Move move; move.push_back(SingleParticleMove(phibeta_particle, r1[*particle1])); phibeta->perform_move(move); phibeta_dirty = true; } if (particle2) { std::unique_ptr<Wavefunction<amplitude_t>::Amplitude> &phibeta = particle2_now_in_subsystem ? phibeta1 : phibeta2; bool &phibeta_dirty = particle2_now_in_subsystem ? phibeta1_dirty : phibeta2_dirty; const Particle phibeta_particle = particle2_now_in_subsystem ? Particle(copy1_subsystem_indices[particle2->species][pairing_index2], particle2->species) : *particle2; // the only time both particles will move here is when delta == 1, // in which case this phibeta and the phibeta above will be // different, so we know that phibeta_dirty will never be true // here. assert(!phibeta_dirty); Move move; move.push_back(SingleParticleMove(phibeta_particle, r2[*particle2])); phibeta->perform_move(move); phibeta_dirty = true; } } }