def __init__( self, Theta, e7=e7, verbose=False, check_gaugeability=True, gaugeability_atol=1e-8, # Either `None`, or ('SUSY', None), # or ('M2G', {[8]-sequence of masses}). stationarity_tweak=None): super().__init__(e7.t56r, verbose=verbose) if check_gaugeability: if get_gaugeability_condition_violations(Theta, e7=e7, atol=gaugeability_atol): raise ValueError('Non-gaugeable Theta-tensor provided.') self._stationarity_tweak = stationarity_tweak self._opt_tc_stationarity_tweak = ( None if stationarity_tweak is None or stationarity_tweak[1] is None else mu.tff64(stationarity_tweak[1])) self._tc_X = tc_X = mu.tff64(get_X(Theta, e7=e7)) self._tc_XX = tf.einsum('MNQ,PQN->MP', tc_X, tc_X) self._tc_e7_S_rc = tf.constant(e7.S_rc, dtype=tf.complex128) self._tc_1j = mu.tfc128(0, 1) self._tc_28_8_8 = tf.constant(algebra.g.su8.m_28_8_8, dtype=tf.complex128) self._tc_56_888 = tf.constant(algebra.g.su8.m_56_8_8_8, dtype=tf.complex128) self._tc_eps_56_56_8_8 = tf.constant(algebra.g.su8.eps_56_56_8_8, dtype=tf.complex128) self._tc_omega = tf.constant(e7.omega, dtype=tf.complex128)
def align_proj133o(proj133o_target, proj133o, e7=e7, debug=True): """Finds an E7-split-133o-rotation that aligns two 133o-projectors.""" # 'Split rotation' here means that on an ^a-index, we first apply a # 'compact SU(8)' and then a 'noncompact E7' rotation. tc_fo_abC = mu.tff64(e7.fo_abC) tc_proj133o_target = mu.tff64(proj133o_target) tc_proj133o = mu.tff64(proj133o) def tf_rotated(t_133o): t_gen_noncompact = tf.einsum('abC,a->Cb', tc_fo_abC[:70, :, :], t_133o[:70]) t_gen_compact = tf.einsum('abC,a->Cb', tc_fo_abC[70:, :, :], t_133o[70:]) t_rot = ( tf.linalg.expm(t_gen_noncompact) @ tf.linalg.expm(t_gen_compact)) t_rot_inv = ( tf.linalg.expm(-t_gen_compact) @ tf.linalg.expm(-t_gen_noncompact)) return t_rot @ tc_proj133o @ t_rot_inv def tf_rotation_loss(t_133o): t_rotated = tf_rotated(t_133o) t_loss = tf.math.reduce_sum( tf.math.square(t_rotated - tc_proj133o_target)) if debug: print(f'Loss: {t_loss.numpy():.6g}') return t_loss opt_val, opt_rot = mu.tf_minimize_v2( tf_rotation_loss, mu.rng(0).normal(size=133, scale=1e-3)) # Note that output-index of the projector is ^a. To this, we apply NC @ C. return opt_val, opt_rot, tf_rotated(mu.tff64(opt_rot)).numpy()
def get_subspace_aligner(self, target_subspace_an, rcond=1e-10): """Returns a closure that aligns a scalar vector with a target space.""" target_subspace_an = numpy.asarray(target_subspace_an) if target_subspace_an.shape[0] != 70 or len(target_subspace_an.shape) != 2: raise ValueError( 'Target subspace must be a [70, D]-array, ' f'shape is: {target_subspace_an.shape}') tc_f_abC = mu.tff64(self.e7.f_abC) v70o_target_subspace_an = mu.nsum( 'an,Aa->An', target_subspace_an, self.e7.v70o_from_v70) svd_u, svd_s, svd_vh = numpy.linalg.svd(v70o_target_subspace_an, full_matrices=False) del svd_vh # Unused, named for documentation purposes only. v70o_target_subspace_an_basis = svd_u[:, svd_s > rcond] tc_v70o_proj_complement = mu.tff64( numpy.eye(70) - v70o_target_subspace_an_basis.dot(v70o_target_subspace_an_basis.T)) tc_v70o_from_v70 = mu.tff64(self.e7.v70o_from_v70) # def f_do_align(v70, **kwargs): tc_v70 = mu.tff64(v70) def tf_loss(t_rot_params): t_gen_so8 = tf.einsum('abC,a->Cb', tc_f_abC[-28:, :70, :70], t_rot_params) t_rot_so8 = tf.linalg.expm(t_gen_so8) t_rotated = tf.einsum('ab,b->a', t_rot_so8, tc_v70) t_deviation = tf.einsum( 'a,Aa,BA->B', t_rotated, tc_v70o_from_v70, tc_v70o_proj_complement) return tf.reduce_sum(tf.math.square(t_deviation)) return mu.tf_minimize_v2(tf_loss, v70, **kwargs) # return f_do_align
def align_thetas(theta_to_align, thetas_target, e7=e7, debug=True, maxiter=10**4, x0_hint=None, strategy=(('BFGS', None), ), seed=11): """Aligns a linear combination of thetas_input with thetas_target.""" # thetas_target.shape == (num_thetas, 56, 133). t0 = t_now = time.time() num_thetas = thetas_target.shape[0] tf_turners = tf_e7_turners_56_133o(e7=e7) # 'Split rotation' here means that on an ^a-index, we first apply a # 'compact SU(8)' and then a 'noncompact E7' rotation. theta_to_align_56x133o = mu.nsum('Ma,ca->Mc', theta_to_align, e7.v133o_from_v133) tc_theta_to_align_56x133o = mu.tff64(theta_to_align_56x133o) thetas_target_56x133o = mu.nsum('nMa,ca->nMc', thetas_target, e7.v133o_from_v133) tc_thetas_target_56x133o = mu.tff64(thetas_target_56x133o) def tf_rotated(t_133o): t_rot_133o, t_rot_56 = tf_turners(t_133o) return tf.einsum('Ma,ba,MN->Nb', tc_theta_to_align_56x133o, t_rot_133o, t_rot_56) def tf_rotation_loss(t_133ox): nonlocal t_now t_rotated = tf_rotated(t_133ox[:133]) t_target = mu.nsum('nMa,n->Ma', tc_thetas_target_56x133o, t_133ox[133:]) t_loss = tf.math.reduce_sum(tf.math.square(t_rotated - t_target)) if debug: t_next = time.time() print(f'T={t_next - t0:8.3f} sec (+{t_next - t_now:8.3f} sec) ' f'Loss: {t_loss.numpy():.12g}') t_now = t_next return t_loss if x0_hint is not None: x0 = numpy.asarray(x0_hint) else: x0 = (mu.rng(seed).normal(size=133, scale=0.05).tolist() + mu.rng(seed + 1).normal(size=num_thetas, scale=0.25).tolist()) opt_val, opt_133ox = mu.tf_minimize_v2(tf_rotation_loss, x0, strategy=strategy, default_maxiter=maxiter, default_gtol=1e-14) # Note that output-index of the projector is ^a. # To this, we apply NC @ C. return (opt_val, opt_133ox, numpy.concatenate( [e7.v133_from_v133o.dot(opt_133ox[:133]), opt_133ox[133:]], axis=0), mu.nsum('Ma,ba->Mb', tf_rotated(mu.tff64(opt_133ox[:133])).numpy(), e7.v133_from_v133o))
def _canonicalize_equilibrium_sc(self, v70, diagonalize_8x8s=True, rng=None, verbose=True): """Simplifies a location on the scalar manifold by rotation.""" if rng is None: rng = numpy.random.RandomState() m8x8s = mu.nsum('Aij,A->ij', self.e7.su8.m_35_8_8.real, v70[:35]) m8x8c = mu.nsum('Aij,A->ij', self.e7.su8.m_35_8_8.real, v70[35:]) rot = self.e7.spin8.get_diagonalizing_rotation( m8x8s if diagonalize_8x8s else m8x8c) decomposed_rot = mu.product_decompose_rotation(rot) resynthesized_rot = mu.resynthesize_rotation_for_rep( 8, 8, decomposed_rot, 'ab,->ab', numpy.ones([])) if not numpy.allclose(rot, resynthesized_rot, rtol=1e-3, atol=1e-5): raise ValueError( 'Resynthesized rotation does not match original rotation.') generator_mapping_spec = 'sS,sScC->cC' if diagonalize_8x8s else 'cC,sScC->sS' rep_action = 0.25 * self.e7.spin8.gamma_sscc rot_other_rep = mu.resynthesize_rotation_for_rep( 8, 8, decomposed_rot, generator_mapping_spec, rep_action) (rot_s, rot_c) = ((rot, rot_other_rep) if diagonalize_8x8s else (rot_other_rep, rot)) canon_m8x8s = rot_s.T @ m8x8s @ rot_s canon_m8x8c = rot_c.T @ m8x8c @ rot_c if diagonalize_8x8s: gens_postdiag = mu.get_generators_for_post_diagonalization_reduction( numpy.diag(canon_m8x8s), 'gsS,sScC->gcC', self.e7.spin8.gamma_sscc) else: gens_postdiag = mu.get_generators_for_post_diagonalization_reduction( numpy.diag(canon_m8x8c), 'gcC,sScC->gsS', self.e7.spin8.gamma_sscc) tc_rot_gens = mu.tff64(gens_postdiag) tc_8x8s = mu.tff64(canon_m8x8s) tc_8x8c = mu.tff64(canon_m8x8c) @tf.function def tf_rotated_8x8(t_rot_params): t_rot = mu.tf_expm( tf.einsum('gab,g->ab', tc_rot_gens, t_rot_params)) if diagonalize_8x8s: tc_rotated_8x8 = tf.linalg.matmul( t_rot @ tc_8x8c, t_rot, transpose_b=True) else: tc_rotated_8x8 = tf.linalg.matmul( t_rot @ tc_8x8s, t_rot, transpose_b=True) return tc_rotated_8x8 @tf.function def tf_loss(t_rot_params): t_8x8 = tf_rotated_8x8(t_rot_params) ret = tf.reduce_sum(tf.abs(t_8x8)) return ret if gens_postdiag.shape[0] == 0: return self.e7.v70_from_35s35c(canon_m8x8s, canon_m8x8c) _, opt_rot_params = mu.tf_minimize_v2( tf_loss, rng.normal(scale=1.0, size=gens_postdiag.shape[0]), default_gtol=1e-14) opt_8x8 = tf_rotated_8x8(mu.tff64(opt_rot_params)).numpy() if diagonalize_8x8s: return self.e7.v70_from_35s35c(canon_m8x8s, opt_8x8) else: return self.e7.v70_from_35s35c(opt_8x8, canon_m8x8c)
def tf_sugra_tensors_from_vielbein(self, t_vielbein, t_omega=None): """See base class.""" t_T = self.tf_T(t_vielbein, t_omega=t_omega) t_A1, t_A2, _ = self.tf_A123(t_T, want_A3=False) t_potential_A1 = mu.tff64(-3 / 4) * tf.math.real( tf.einsum('ij,ij->', t_A1, tf.math.conj(t_A1))) t_potential_A2 = mu.tff64(1 / 24) * tf.math.real( tf.einsum('ijkl,ijkl->', t_A2, tf.math.conj(t_A2))) t_potential = t_potential_A1 + t_potential_A2 return t_potential, t_T, t_A1, t_A2
def tf_stationarity_internal(self, t_potential, t_grad_potential, t_vielbein): """Computes the stationarity-violation.""" t_stat = super().tf_stationarity_internal(t_potential, t_grad_potential, t_vielbein) if self._stationarity_tweak is None: return tf.math.asinh(t_stat) # Squashed. else: t_A1, *_ = self.tf_A123(self.tf_T(t_vielbein), want_A1=True, want_A2=False, want_A3=False) t_m2grav = self.tf_gravitino_masses(t_A1, t_potential) if self._stationarity_tweak[0] == 'SUSY': t_lightest_gravitino_mass = t_m2grav[-1] t_ret = t_stat * tf.clip_by_value(t_lightest_gravitino_mass, 1.0, 5.0) return tf.math.asinh(t_ret) # Double-squash elif self._stationarity_tweak[0] == 'M2G': t_spectrum_mismatch = tf.math.reduce_sum( tf.math.square(t_m2grav - self._stationarity_tweak[1])) return (tf.math.asinh(t_stat) + tf.math.asinh(t_spectrum_mismatch) * mu.tff64(0.1)) else: raise ValueError('Unknown stationarity-tweak.')
def tf_sugra_tensors_from_vielbein(self, t_V): """See base class.""" # We also need the inverse Vielbein. # Here, we make use of the inverse of a symplectic matrix # [[A, B], [C, D]] # being given by: # [[D.T, -B.T], [-C.T, A.T]]. t_Vi = tf.reshape( tf.stack([ tf.stack([ tf.transpose(t_V[28:, 28:]), -tf.transpose(t_V[:28, 28:]) ], axis=1), tf.stack([ -tf.transpose(t_V[28:, :28]), tf.transpose(t_V[:28, :28]) ], axis=1) ], axis=0), (56, 56)) t_M = tf.einsum('RX,SX->RS', t_V, t_V) t_Minv = tf.einsum('XR,XS->RS', t_Vi, t_Vi) # Potential (2.9) in arXiv:1112.3345 - also, (4.49) in arXiv:0705.2101. t_potential = mu.tff64(1 / 168.0) * (tf.einsum( 'mnr,mM,nN,rR,MNR->', self._tc_X, t_Minv, t_Minv, t_M, self._tc_X) + 7 * tf.einsum('MP,MP->', self._tc_XX, t_Minv)) return (t_potential, ) # 1-tuple.
def tf_dwn_stationarity_vec(t_A1, t_A2): """Computes stationarity-violation 70-vector 'in the local frame'.""" # Formula: # arXiv: https://arxiv.org/pdf/1302.6219.pdf, formula (3.2); # originally: https://inspirehep.net/literature/191530 # (2.20) - (2.22). _m_8888sd_35ortho, _m_8888asd_35ortho = _get_35sd_asd_bases() t_x0 = (tf.einsum('mi,mjkl->ijkl', t_A1, t_A2) + mu.tfc128(-0.75, 0.0) * tf.einsum('mnij,nklm->ijkl', t_A2, t_A2)) t_x0_real = tf.math.real(t_x0) t_x0_imag = tf.math.imag(t_x0) # The self-dual part must be zero. t_x0_re_sd = tf.einsum('ijkl,ijklX->X', t_x0_real, mu.tff64(_m_8888sd_35ortho)) t_x0_im_asd = tf.einsum('ijkl,ijklX->X', t_x0_imag, mu.tff64(_m_8888asd_35ortho)) return tf.concat([t_x0_re_sd, t_x0_im_asd], axis=0)
def test_known_so8c_solutions(self): """Asserts numpy stationarity of the known SO(8)c omega=pi/8 solutions.""" tc_omega = mu.tff64(numpy.pi / 8) for row in mu.csv_numdata('dim4/so8/equilibria/SO8C_PI8_SOLUTIONS.csv'): table_potential = row[0] potential, stationarity = SUGRA.potential_and_stationarity( row[2:], t_omega=tc_omega) self.assertTrue(0.0 <= stationarity <= 1e-7) self.assertTrue(numpy.isclose(potential, table_potential, atol=1e-8))
def holomorphic_w_sugra(zs): gs = mu.undiskify(zs) * 0.25 v70 = mu.nsum('zna,zn->a', algebra.g.e7.sl2x7[:2, :, :70], numpy.stack([gs.real, gs.imag], axis=0)) A1 = SUGRA.tf_ext_sugra_tensors(mu.tff64(v70), with_stationarity=False)[2].numpy() kaehler_factor = numpy.sqrt((1 - zs * zs.conjugate()).prod()) return a123.superpotential(A1, direction_A1) * kaehler_factor
def test_one_nontrivial_solution(self): """Asserts numpy stationarity of a nontrivial solution.""" t_v70 = mu.tff64(_EXAMPLE_SOLUTION) tensors_stat = SUGRA.tf_ext_sugra_tensors(t_v70, with_stationarity=True) tensors_nostat = SUGRA.tf_ext_sugra_tensors(t_v70, with_stationarity=False) self.assertTrue(0 <= tensors_stat[-1].numpy() <= 1e-3) self.assertTrue(-8.473 <= tensors_stat[0].numpy() <= -8.472) self.assertTrue(numpy.isnan(tensors_nostat[-1].numpy())) self.assertTrue(numpy.allclose(tensors_stat[0].numpy(), tensors_nostat[0].numpy()))
def tf_e7_turners_56_133o(e7=e7): """Returns closure that maps 70+63 e7-rotation to _56 and ^133o matrices.""" # This is a somewhat technical helper function which however # is useful to have in quite a few places. tc_fo_abC = mu.tff64(e7.fo_abC) tc_to56r = mu.tff64(mu.nsum('aMN,ac->cMN', e7.t56r, e7.v133_from_v133o)) def tf_turners(t_133o, order133_c_first=True): """Returns 133o x 133o and 56 x 56 rotation matrices. Args: t_133o: numpy.ndarray, rotation-parameters in the 133o-irrep of e7 from which we build two rotations (per Theta-index, so four in total), one for the compact and one for the noncompact part of the algebra. order133_c_first: If true, the 133o-rotation is built by first performing the compact, then the noncompact rotation. (Useful for taking inverses.) Returns: A pair `(t_rot_133o, t_rot_56)` of a [133, 133]-float64-tf.Tensor `t_rot133o` rotating the 133o-index on Theta and a [56, 56]-float64-tf.Tensor rotating the 56-index on Theta. """ t_70 = t_133o[:70] t_63 = t_133o[70:] t_rot_nc133o = tf.linalg.expm( tf.einsum('abC,a->Cb', tc_fo_abC[:70], t_70)) t_rot_c133o = tf.linalg.expm( tf.einsum('abC,a->Cb', tc_fo_abC[70:], t_63)) t_rot_nc56 = tf.linalg.expm( tf.einsum('aMN,a->NM', tc_to56r[:70], -t_70)) t_rot_c56 = tf.linalg.expm(tf.einsum('aMN,a->NM', tc_to56r[70:], -t_63)) if order133_c_first: t_rot_133o = t_rot_nc133o @ t_rot_c133o t_rot_56 = t_rot_c56 @ t_rot_nc56 else: t_rot_133o = t_rot_nc133o @ t_rot_c133o t_rot_56 = t_rot_c56 @ t_rot_nc56 return t_rot_133o, t_rot_56 return tf_turners
def test_origin_so8_solution(self): """Asserts properties of the solution at the origin.""" t_v70 = mu.tff64([0] * 70) pot, tt, a1, a2, stat = [x.numpy() for x in SUGRA.tf_ext_sugra_tensors(t_v70)] self.assertTrue(numpy.allclose(-6.0, pot)) self.assertTrue(0 <= stat <= 1e-20) self.assertTrue(numpy.allclose(numpy.eye(8), a1)) self.assertTrue(numpy.allclose(0, a2)) tt_entries = sorted(collections.Counter(tt.ravel()).items()) self.assertEqual([(-0.75, 56), (0, 3984), (0.75, 56)], tt_entries)
def f_do_align(v70, **kwargs): tc_v70 = mu.tff64(v70) def tf_loss(t_rot_params): t_gen_so8 = tf.einsum('abC,a->Cb', tc_f_abC[-28:, :70, :70], t_rot_params) t_rot_so8 = tf.linalg.expm(t_gen_so8) t_rotated = tf.einsum('ab,b->a', t_rot_so8, tc_v70) t_deviation = tf.einsum( 'a,Aa,BA->B', t_rotated, tc_v70o_from_v70, tc_v70o_proj_complement) return tf.reduce_sum(tf.math.square(t_deviation)) return mu.tf_minimize_v2(tf_loss, v70, **kwargs)
def tf_T(self, t_vielbein, t_omega=None): """Computes the SO(8) T-tensor.""" t_omega = mu.tff64(0.0) if t_omega is None else t_omega t_u_ijIJ = self._expand_ijkl(t_vielbein[:28, :28]) t_u_klKL = tf.math.conj(t_u_ijIJ) t_v_ijKL = self._expand_ijkl(t_vielbein[:28, 28:]) t_v_klIJ = tf.math.conj(t_v_ijKL) t_cw = tf.math.cos(t_omega) t_sw = tf.math.sin(t_omega) tc_exp_w = tf.complex(t_cw, t_sw) tc_exp_nw = tf.complex(t_cw, -t_sw) t_uv = tc_exp_nw * t_u_klKL + tc_exp_w * t_v_klIJ t_uuvv = ( tf.einsum('lmJK,kmKI->lkIJ', t_u_ijIJ, t_u_klKL) - tf.einsum('lmJK,kmKI->lkIJ', t_v_ijKL, t_v_klIJ)) return tf.einsum('ijIJ,lkIJ->lkij', t_uv, t_uuvv)
def refine_omega_zs(omega, zs, verbosity='SF', sugra=None, e7=None, debug=False): """Refines z-coordinate vectors to low stationarity-violation.""" if e7 is None: e7 = algebra.g.e7 if sugra is None: sugra = analysis.SO8_SUGRA(e7=e7) cs = numpy.array( # Factor 0.25 is due to normalization of SL(2)-generators. [0.25 * mu.undiskify(z / max(1, abs(z) + 1e-5)) for z in zs]) current_opt_stat = 1.0 current_opt_pos = numpy.concatenate([cs.real, cs.imag], axis=0) # It is very important that we do get a good-quality equilibrium here. # If we do not, this would seriously mess up convergence acceleration # as we use it to distill out the boundary-tensor. while current_opt_stat > 1e-12: opt = sugra.find_equilibrium( current_opt_pos, verbosity=verbosity, submanifold_embedding=e7.sl2x7[:2, :, :70].reshape(14, 70), t_omega=mu.tff64(omega), minimize_kwargs=dict(default_gtol=1e-14, default_maxiter=10**5)) current_opt_pot, current_opt_stat, current_opt_pos = opt if debug: print(f'Debug: refined opt_pot={current_opt_pot}, ' f'opt_stat={current_opt_stat}') refined_zs = numpy.array([ mu.diskify(4 * z) for z in current_opt_pos[:7] + 1j * current_opt_pos[7:] ]) return omega, current_opt_pot, current_opt_stat, refined_zs
ds_step = 0.003 scan_boundary_gauging_num_samples = 50 scan_file = os.path.join(target_dir, 'u4xr12_equilibria.csv') analyzed_file = os.path.join(target_dir, 'u4xr12_equilibria_analyzed.pytxt') os.makedirs(target_dir, exist_ok=True) if mu.arg_enabled(__name__, 'compute_trajectory'): print('# Computing SO(3) N=1 trajectory on SL2x7...') v14 = analyze_sl2x7.v14_from_7z(analyze_sl2x7.get_7z_from_bfp_z123( # Numbers match Eq. (4.31) in BFP, https://arxiv.org/abs/1909.10969 (0.1696360+0.1415740j, 0.4833214+0.3864058j, -0.3162021-0.5162839j))) v70_so3n1 = subspace_an.dot(v14) # Check that we do have the correct equilibrium. pot, stat = sugra.potential_and_stationarity(v70_so3n1, t_omega=mu.tff64(0.0)) assert abs(-13.84096 - pot) < 1e-4 and stat < 1e-8 dyonic.analyze_omega_deformation( mu.home_relative(target_dir), v70_so3n1, ds=ds_step) glob_pos, glob_neg = ( os.path.join(target_dir, f'S1384096/omega_0.0000_{tag}_*.log') for tag in ('pos', 'neg')) tdata = dyonic.collect_trajectory_logs(glob_pos, glob_neg) numpy.save(trajectory_npy_filename, tdata) if mu.arg_enabled(__name__, 'extrapolate_and_plot'): print('# Extrapolating trajectory and plotting...') tdata = numpy.load(trajectory_npy_filename)
def pot_stat_7z(zs, omega): return sugra.potential_and_stationarity(v70_from_7z(zs, e7=sugra.e7), t_omega=mu.tff64(omega))
def tf_dwn_stationarity(t_A1, t_A2): """Computes stationarity-violation 'in the local frame'.""" return (tf.math.reduce_sum( tf.math.square(tf_dwn_stationarity_vec(t_A1, t_A2))) / mu.tff64(48.0))