def covDeriv(self, scalarField, gaugeField, cpt): scalarFieldShifted = self.shiftScalarField(scalarField, cpt, +1) lieAlgField = scalarField * FieldTools.pauliMatrix(3) lieAlgFieldShifted = scalarFieldShifted * FieldTools.pauliMatrix(3) covDeriv = gaugeField[:,:,:,cpt,:,:] @ lieAlgFieldShifted @\ tf.linalg.adjoint(gaugeField[:,:,:,cpt,:,:]) - lieAlgField return covDeriv
def interp1d(scalarField, gaugeField, axis, theory): inputLatShape = tf.shape(scalarField)[0:3] outputLatShape = inputLatShape outputLatShape = tf.tensor_scatter_nd_update(outputLatShape, [[axis]], [2*outputLatShape[axis]]) outputScalarField = tf.zeros(tf.concat([outputLatShape, [1, 1]], 0), dtype=tf.complex128) outputGaugeField = tf.zeros(tf.concat([outputLatShape, [3, 2, 2]], 0), dtype=tf.complex128) inputIndexVectors = [tf.range(inputLatShape[0]), tf.range(inputLatShape[1]), tf.range(inputLatShape[2])] inputIndices = tf.stack(tf.meshgrid(inputIndexVectors[0], inputIndexVectors[1], inputIndexVectors[2], indexing="ij"), -1) outputIndexVectorsOdd = inputIndexVectors.copy() outputIndexVectorsOdd[axis] = 2*inputIndexVectors[axis] + 1 outputIndicesOdd = tf.stack(tf.meshgrid(outputIndexVectorsOdd[0], outputIndexVectorsOdd[1], outputIndexVectorsOdd[2], indexing="ij"), -1) originalScalarVals = tf.gather_nd(scalarField, inputIndices) originalGaugeVals = tf.gather_nd(gaugeField, inputIndices) outputScalarField = tf.tensor_scatter_nd_update(outputScalarField, outputIndicesOdd, originalScalarVals) outputGaugeField = tf.tensor_scatter_nd_update(outputGaugeField, outputIndicesOdd, originalGaugeVals) scalarFieldShifted = theory.shiftScalarField(scalarField, axis, -1) gaugeFieldShifted = theory.shiftGaugeField(gaugeField, axis, -1) avgScalarField = 0.5*(scalarField + scalarFieldShifted) avgGaugeField = FieldTools.linearAverage(gaugeField, gaugeFieldShifted) interpScalarVals = tf.gather_nd(avgScalarField, inputIndices) interpGaugeVals = tf.gather_nd(avgGaugeField, inputIndices) outputIndexVectorsEven = inputIndexVectors.copy() outputIndexVectorsEven[axis] = 2*inputIndexVectors[axis] outputIndicesEven = tf.stack(tf.meshgrid(outputIndexVectorsEven[0], outputIndexVectorsEven[1], outputIndexVectorsEven[2], indexing="ij"), -1) outputScalarField = tf.tensor_scatter_nd_update(outputScalarField, outputIndicesEven, interpScalarVals) outputGaugeField = tf.tensor_scatter_nd_update(outputGaugeField, outputIndicesEven, interpGaugeVals) return outputScalarField, outputGaugeField
def shiftPlaquette(self, plaquette, plaqDir1, plaqDir2, shiftDir, sign): # Moving one site forwards is equivalent to shifting the whole field # backwards, hence the minus sign (active/passive transform) plaquetteShifted = tf.roll(plaquette, -sign, shiftDir) if shiftDir != 0: return plaquetteShifted indices = FieldTools.boundaryIndices(self.latShape, shiftDir, sign) if sign == -1 and (plaqDir1 == 0 or plaqDir2 == 0): # Set the plaquettes straddling the origin to the identity updates = tf.eye(2, batch_shape=tf.shape(indices)[0:-1], dtype=tf.complex128) plaquetteShifted = tf.tensor_scatter_nd_update( plaquetteShifted, indices, updates) else: # Apply reflecting BC's by setting links at the boundary to # corresponding values from unshifted field updates = tf.gather_nd(plaquette, indices) plaquetteShifted = tf.tensor_scatter_nd_update( plaquetteShifted, indices, updates) return plaquetteShifted
def shiftGaugeField(self, gaugeField, cpt, sign): gaugeFieldShifted = tf.roll(gaugeField, -sign, cpt) pauliMatNum = self.boundaryConditions[cpt] if pauliMatNum == 0: return gaugeFieldShifted latShape = tf.shape(gaugeField)[0:3] indices = FieldTools.boundaryIndices(latShape, cpt, sign) updates = tf.gather_nd(gaugeFieldShifted, indices) updates = FieldTools.pauliMatrix(pauliMatNum) @\ updates @ FieldTools.pauliMatrix(pauliMatNum) gaugeFieldShifted = tf.tensor_scatter_nd_update( gaugeFieldShifted, indices, updates) return gaugeFieldShifted
def processGradients(self, grads, fields): processedGrads = grads processedGrads[0] = grads[0] / (2 * np.pi * tf.cast( tf.reshape(self.metric, tf.concat([self.latShape, [1, 1]], 0)), tf.complex128)) processedGrads[1] = FieldTools.projectSu2Gradients(grads[1], fields[1]) processedGrads[1] = processedGrads[1] / (2 * np.pi * tf.cast( tf.reshape(self.metric, tf.concat([self.latShape, [1, 1, 1]], 0)), tf.complex128)) return processedGrads
def shiftGaugeField(self, gaugeField, cpt, sign): # Moving one site forwards is equivalent to shifting the whole field # backwards, hence the minus sign (active/passive transform) gaugeFieldShifted = tf.roll(gaugeField, -sign, cpt) if cpt != 0: return gaugeFieldShifted # Apply reflecting BC's by setting links at the boundary to # corresponding values from unshifted field if sign == +1: slicePos = self.latShape[cpt] - 2 else: slicePos = 1 # For gathering from the shifted field (gathering from the variable is slow) indices = FieldTools.sliceIndices(self.latShape, cpt, slicePos) # For scattering onto the boundary boundaryIndices = FieldTools.boundaryIndices(self.latShape, cpt, sign) updates = tf.gather_nd(gaugeFieldShifted, indices) boundaryUpdates = tf.gather_nd(gaugeField, boundaryIndices) gaugeFieldShifted = tf.tensor_scatter_nd_update( gaugeFieldShifted, boundaryIndices, updates) if sign == -1: # Set the r-links at the origin to the identity rOriginIndices = tf.stack( tf.meshgrid(0, tf.range(self.latShape[1]), tf.range(self.latShape[2]), 0, indexing="ij"), -1) rOriginUpdates = tf.eye(2, batch_shape=tf.shape(rOriginIndices)[0:-1], dtype=tf.complex128) gaugeFieldShifted = tf.tensor_scatter_nd_update( gaugeFieldShifted, rOriginIndices, rOriginUpdates) return gaugeFieldShifted
def shiftScalarField(self, scalarField, cpt, sign): # Moving one site forwards is equivalent to shifting the whole field # backwards, hence the minus sign (active/passive transform) scalarFieldShifted = tf.roll(scalarField, -sign, cpt) if cpt != 0: return scalarFieldShifted # Apply reflecting BC's by setting links at the boundary to # corresponding values from unshifted field if sign == +1: slicePos = self.latShape[cpt] - 2 else: slicePos = 1 # For gathering from the shifted field (gathering from the variable is slow) indices = FieldTools.sliceIndices(self.latShape, cpt, slicePos) # For scattering onto the boundary boundaryIndices = FieldTools.boundaryIndices(self.latShape, cpt, sign) updates = tf.gather_nd(scalarFieldShifted, indices) scalarFieldShifted = tf.tensor_scatter_nd_update( scalarFieldShifted, boundaryIndices, updates) return scalarFieldShifted
def shiftScalarField(self, scalarField, cpt, sign): scalarFieldShifted = tf.roll(scalarField, -sign, cpt) pauliMatNum = self.boundaryConditions[cpt] # Only requires flipping if third pauli matrix is used if pauliMatNum != 3: return scalarFieldShifted latShape = tf.shape(scalarField)[0:-2] indices = FieldTools.boundaryIndices(latShape, cpt, +1) updates = -1.0 * tf.gather_nd(scalarFieldShifted, indices) scalarFieldShifted = tf.tensor_scatter_nd_update( scalarFieldShifted, indices, updates) return scalarFieldShifted
def shiftCovDeriv(self, covDeriv, cpt, sign): covDerivShifted = tf.roll(covDeriv, -sign, cpt) if cpt != 0: return covDerivShifted indices = FieldTools.boundaryIndices(self.latShape, cpt, sign) if sign == -1: updates = tf.zeros(tf.concat([tf.shape(indices)[0:-1], [2, 2]], 0), dtype=tf.complex128) else: updates = tf.gather_nd(covDeriv, indices) covDerivShifted = tf.tensor_scatter_nd_update(covDerivShifted, indices, updates) return covDerivShifted
def covDerivTerm(self, higgsField, isospinField, hyperchargeField): energyDensity = tf.zeros(tf.shape(higgsField)[0:3], dtype=tf.float64) higgsMagnitude = tf.linalg.trace( self.higgsMagnitude(tf.math.real(higgsField))) numDims = 3 # This is only valid in the unitary gauge, but it keeps the isospin # gradients symmetric which is good for convergence to the saddle for ii in range(numDims): higgsFieldShifted = self.shiftHiggsField(higgsField, ii, +1) higgsMagnitudeShifted = tf.linalg.trace( self.higgsMagnitude(tf.math.real(higgsFieldShifted))) energyDensity += higgsMagnitude**2 energyDensity += higgsMagnitudeShifted**2 energyDensity -= higgsMagnitude * higgsMagnitudeShifted *\ tf.math.real(tf.linalg.trace(isospinField[:,:,:,ii,:,:])) *\ tf.math.real(tf.linalg.trace(hyperchargeField[:,:,:,ii,:,:])) energyDensity += higgsMagnitude * higgsMagnitudeShifted *\ tf.math.imag(tf.linalg.trace(isospinField[:,:,:,ii,:,:] @\ FieldTools.pauliMatrix(3))) *\ tf.math.imag(tf.linalg.trace(hyperchargeField[:,:,:,ii,:,:])) return energyDensity
# Shift the poles around, obeying the BCs leftGaugeField = inputGaugeField leftScalarField = inputScalarField rightGaugeField = inputGaugeField rightScalarField = inputScalarField for ii in range(shiftNumLeft): leftGaugeField = singlePoleTheory.shiftGaugeField(leftGaugeField, 0, +1) leftScalarField = singlePoleTheory.shiftScalarField(leftScalarField, 0, +1) # Because shiftNumRight > monopoleXCoord, the monopole ends up conjugated for ii in range(shiftNumRight): rightGaugeField = singlePoleTheory.shiftGaugeField(rightGaugeField, 0, +1) rightScalarField = singlePoleTheory.shiftScalarField( rightScalarField, 0, +1) gaugeField = FieldTools.linearSuperpose(leftGaugeField, rightGaugeField) scalarField = 0.5 * (leftScalarField + rightScalarField) # Shift once more to centre the pair gaugeField = tf.roll(gaugeField, -1, 0) scalarField = tf.roll(scalarField, -1, 0) numFluxQuanta = args.externalField # Negative flux quanta results in lowering of energy (dipole points along # negative x axis) magField = FieldTools.constantMagneticField(X, Y, Z, 0, -numFluxQuanta) gaugeField = FieldTools.linearSuperpose(gaugeField, magField) gaugeFieldVar = tf.Variable(gaugeField, trainable=True) scalarFieldVar = tf.Variable(scalarField, trainable=True)
def processGradients(self, grads, fields): processedGrads = grads processedGrads[1] = FieldTools.projectSu2Gradients(grads[1], fields[1]) return processedGrads
gaugeVec0 = tf.zeros([Nx, Ny, Nz, 3], dtype=tf.float64).numpy() gaugeVec1 = tf.zeros([Nx, Ny, Nz, 3], dtype=tf.float64).numpy() gaugeVec2 = tf.zeros([Nx, Ny, Nz, 3], dtype=tf.float64).numpy() # Symmetric perturbation in the centre of the lattice gaugeVec1[Nx//2, Ny//2, Nz//2, 0] += 0.01 gaugeVec1[Nx//2 + 1, Ny//2, Nz//2, 0] += 0.01 gaugeVec1[Nx//2, Ny//2, Nz//2 + 1, 0] += 0.01 gaugeVec1[Nx//2 + 1, Ny//2, Nz//2 + 1, 0] += 0.01 gaugeVec2[Nx//2, Ny//2, Nz//2, 1] += 0.01 gaugeVec2[Nx//2 + 1, Ny//2, Nz//2, 1] -= 0.01 gaugeVec2[Nx//2, Ny//2 + 1, Nz//2, 1] += 0.01 gaugeVec2[Nx//2 + 1, Ny//2 + 1, Nz//2, 1] -= 0.01 gaugeMat0 = FieldTools.vecToSu2(gaugeVec0) gaugeMat1 = FieldTools.vecToSu2(gaugeVec1) gaugeMat2 = FieldTools.vecToSu2(gaugeVec2) gaugeMat = tf.stack([gaugeMat0, gaugeMat1, gaugeMat2], -3) # Convert to tf Variables so gradients can be tracked scalarField = tf.Variable(scalarMat, trainable=True) gaugeField = tf.Variable(gaugeMat, trainable=True) theory = GeorgiGlashowSu2TheoryUnitary(params) @tf.function def lossFn(): return theory.energy(scalarField, gaugeField) energy = lossFn()
avgScalarField = 0.5*(scalarField + scalarFieldShifted) avgGaugeField = FieldTools.linearAverage(gaugeField, gaugeFieldShifted) interpScalarVals = tf.gather_nd(avgScalarField, inputIndices) interpGaugeVals = tf.gather_nd(avgGaugeField, inputIndices) outputIndexVectorsEven = inputIndexVectors.copy() outputIndexVectorsEven[axis] = 2*inputIndexVectors[axis] outputIndicesEven = tf.stack(tf.meshgrid(outputIndexVectorsEven[0], outputIndexVectorsEven[1], outputIndexVectorsEven[2], indexing="ij"), -1) outputScalarField = tf.tensor_scatter_nd_update(outputScalarField, outputIndicesEven, interpScalarVals) outputGaugeField = tf.tensor_scatter_nd_update(outputGaugeField, outputIndicesEven, interpGaugeVals) return outputScalarField, outputGaugeField B = 9 smallMagneticField = FieldTools.constantMagneticField( inputR, inputY, inputZ, 0, -B ) inputGaugeField = FieldTools.linearSuperpose( inputGaugeField, smallMagneticField ) outputScalarField, outputGaugeField = interp1d(inputScalarField, inputGaugeField, 0, theory) outputScalarField, outputGaugeField = interp1d(outputScalarField, outputGaugeField, 1, theory) outputScalarField, outputGaugeField = interp1d(outputScalarField, outputGaugeField, 2, theory) outputLatShape = tf.shape(outputScalarField)[0:3] outputParams = inputParams outputParams["vev"] /= 2 outputParams["latShape"] = outputLatShape r = tf.cast(
y = tf.cast(tf.linspace(-(Ny - 1) / 2, (Ny - 1) / 2, Ny), tf.float64) z = tf.cast(tf.linspace(-(Nz - 1) / 2, (Nz - 1) / 2, Nz), tf.float64) X, Y, Z = tf.meshgrid(x, y, z, indexing="ij") # Theory parameters params = { "vev": args.vev, "selfCoupling": args.selfCoupling, "gaugeCoupling": args.gaugeCoupling } # Set up the initial scalar and gauge fields inputPath = args.inputPath if inputPath == "": scalarField, gaugeField = FieldTools.setMonopoleInitialConditions( X, Y, Z, params["vev"]) else: scalarField = np.load(inputPath + "/scalarField.npy") gaugeField = np.load(inputPath + "/gaugeField.npy") # Convert to tf Variables so gradients can be tracked scalarFieldVar = tf.Variable(scalarField, trainable=True) gaugeFieldVar = tf.Variable(gaugeField, trainable=True) theory = GeorgiGlashowSu2Theory(params) @tf.function def lossFn(): return theory.energy(scalarFieldVar, gaugeFieldVar)
inputGaugeField = np.load(inputPath + "/gaugeField.npy", allow_pickle=True) inputParams = np.load(inputPath + "/params.npy", allow_pickle=True).item() # Halve the lattice and field size inputShape = tf.shape(inputX) R = inputX[int(inputShape[0]) // 2:, ...] Y = inputY[int(inputShape[0]) // 2:, ...] Z = inputZ[int(inputShape[0]) // 2:, ...] latShape = tf.shape(R) scalarField = inputScalarField[int(inputShape[0]) // 2:, ...] gaugeField = inputGaugeField[int(inputShape[0]) // 2:, ...] # Add magnetic field if required numFluxQuanta = args.externalField magField = FieldTools.constantMagneticField(R, Y, Z, 0, numFluxQuanta) gaugeField = FieldTools.linearSuperpose(gaugeField, magField) # Theory parameters params = { "vev": args.vev, "selfCoupling": args.selfCoupling, "gaugeCoupling": args.gaugeCoupling, "latShape": latShape } theory = GeorgiGlashowRadialTheory(params) scalarFieldVar = tf.Variable(scalarField) gaugeFieldVar = tf.Variable(gaugeField)