Source code for MonteCarloAlgorithm.isingHamiltonian

import copy
import math
import random
import numpy as np;
[docs]class IsingHamiltonian: random.seed(3) """ creates the ising hamiltonian node connections = the weights of the node connections outside affect = the affect of outside bias on nodal connections """
[docs] def __init__(self, node_connections=[[()]], outside_affect=np.zeros(1)): """ creates a hamiltonian Parameters ---------- node_connections how the nodes connect to each other outside_affect any influence outside the hamiltonian """ assert(len(node_connections) == len(outside_affect)) self.nodeConnections = node_connections self.outsideAffect = outside_affect
[docs] def energy(self, config): """ calculates the energy of the config Parameters ---------- config a bitstring Returns ------- the energy of the config """ assert len(config) == len(self.nodeConnections), "config length does not match" energy = np.dot(self.outsideAffect, 2*config.string-1) # adds the energy of the effects of each other for node in range(len(config)): for n in self.nodeConnections[node]: if n[0] < node: energy += n[1] * (1 if (config.string[node] == config.string[n[0]]) else -1) return energy
[docs] def energy_change(self, config, flipped_index : int, old_energy=float('nan')): """ calculates delta energy given a bit flip Parameters ---------- config origin config flipped_index index of proposed bit to flip old_energy energy state of the current config (not required input) Returns delta energy ------- """ origin_energy = old_energy if not math.isnan(old_energy) else self.energy(config); config.flip(flipped_index); new_energy = self.energy(config) return new_energy - origin_energy, config, new_energy
[docs] def metropolis_sweep(self, config, temperature=1.0): """ runs a metropolis sweep on the bitstring Parameters ---------- config input config temperature temperature Returns ------- changed config """ orgEnergy = self.energy(config); for sweep in random.sample(list(range(len(config))), len(config)): delta_energy, potential_new_config, potential_new_energy = \ self.energy_change(copy.deepcopy(config), sweep, old_energy=orgEnergy) if IsingHamiltonian.make_change(delta_energy, temperature): orgEnergy = potential_new_energy config = potential_new_config return config
[docs] def compute_average_values(self, config, temperature=1.0): """ compute the average energy of a hamiltonian Parameters ---------- config a bitstring does not really matter what is inputed temperature temperature Returns ------- average energy, magnetization, heat capacity, and magnetic susceptibility values """ energy_sum = 0.0 energy_squared_sum = 0.0 magnetization_sum = 0.0 magnetization_squared_sum = 0.0 Z_sum = 0.0 # is Z probability? for x in range(2**len(config)): config.set_int(x, digits=len(config)) energy = self.energy(config) Z = np.exp(-energy/temperature) energy_sum += Z * energy energy_squared_sum += energy * energy * Z magnetization = np.sum(2*config.string - 1) magnetization_sum += magnetization * Z magnetization_squared_sum += magnetization * magnetization * Z Z_sum += Z energy_average = energy_sum / Z_sum energy_squared_average = energy_squared_sum / Z_sum magnetization_average = magnetization_sum / Z_sum magnetization_squared_average = magnetization_squared_sum / Z_sum heat_capacity = (energy_squared_average - energy_average**2)/(temperature**2) magnetic_susceptibility = (magnetization_squared_average - magnetization_average**2)/(temperature) return energy_average, magnetization_average, heat_capacity, magnetic_susceptibility
[docs] @staticmethod def make_change(energy_change, temperature): """ determines if a change to the bit string should be made Parameters ---------- energy_change how much energy a flip would save/cost temperature temperature Returns ------- bool whether or not it should be flipped """ return energy_change <= 0 or (np.exp(-energy_change / temperature) > random.random())