コード例 #1
0
def _get_e7r_912_inner(filename, verbose=True, e7=e7):
    """Computes a basis for the E7 912-irrep in 56 x 133."""
    # The 912-irrep is characterized by (2.14) in
    # https://arxiv.org/pdf/1112.3345.pdf /
    # (2.12) in https://arxiv.org/pdf/0705.2101.pdf
    #
    #     (t_b t^a)_M^N Theta_N^b = -0.5 Theta_M^a
    try:
        return numpy.load(filename)['arr_0']
    except (IOError, OSError):
        if verbose:
            print('Need to compute t912.')
    t0 = time.time()
    k_ab = mu.nsum('aMN,bNM->ab', e7.t56r, e7.t56r)
    # Notation in the papers is slightly ambiguous here.
    # We want '_bM^P,_aP^N' here or '_bP^N,_aM^P'?
    # Only the former option is a correct interpretation.
    M0 = mu.nsum('_bM^P,_aP^N,^aA->^M_A^N_b', e7.t56r, e7.t56r,
                 numpy.linalg.inv(k_ab)).reshape(56 * 133, 56 * 133)
    for k in range(56 * 133):
        M0[k, k] += 0.5
    ret = scipy.linalg.null_space(M0.real, rcond=1e-5).T
    if verbose:
        # This computation typically takes ~5 minutes.
        # We hence cache the result to disk (~50M).
        print('Computing the 912-basis took %.3f sec.' % (time.time() - t0))
    numpy.savez_compressed(filename, ret)
    # Note that this null_space is orthonormal.
    return ret
コード例 #2
0
def get_quadratic_constraints(thetas_in_912, e7=e7):
    """For a collection of Theta-tensors, returns the quadratic constraints.

  Note: This is typically a memory-wise expensive operation.

  Args:
    thetas_in_912: [k, 56, 133]-array of k Theta-tensors that must belong
      to the 912-irrep of potentially gaugeable Thetas.
      Here, the 56-index refers to the e7.t56r basis.
    e7: The algebra.E7 instance to use for E7-conventions.

  Returns:
   [c, k, k]-array of quadratic constraints `qcs`.
     The c-th entry represents the c-th quadratic constraint,
     i.e. einsum('pma,p->ma', thetas_in_912, coeffs) is gaugeable if
     einsum('kpq,p,q->k', qcs, coeffs, coeffs) vanishes.
  """
    # arXiv:1112.3345, below (2.15): For Theta-tensors that satisfy
    # the linear 912-constraint, the two quadratic constraints are equivalent, so
    # we only have to check the Omega-constraint: Theta_M^a Theta_N^b Omega^MN = 0
    thetas_qc = mu.nsum('W_M^a,Z_N^b,^MN->WZab', thetas_in_912, thetas_in_912,
                        e7.omega)
    # Symmetrize in W,Z, since we use the same linear combination of thetas
    # for both. Update entries for memory efficiency.
    thetas_qc += mu.nsum('WZab->ZWab', thetas_qc)
    ttsu, ttss, ttsvh = numpy.linalg.svd(thetas_qc.reshape(-1, 133 * 133))
    del ttsvh  # Unused.
    q_constraints = ttsu.T[ttss > 1e-9].reshape([-1] +
                                                [thetas_in_912.shape[0]] * 2)
    for level in (2, 1, 0):
        gc.collect(level)
    return q_constraints
コード例 #3
0
 def test_get_normalizing_subalgebra_generic(self):
     """Normalizing subalgebra is as expected in a generic situation."""
     e7 = algebra.g.e7
     su8 = e7.su8
     so8_algebra = e7.f_abC[-28:, :70, :70]
     # If we pick the subspace of (8s x 8s)_sym_traceless matrices that
     # are block-diagonal with entries only in the top-left 3x3 block,
     # then there is an obvious SO(5) centralizer and an obvious
     # SO(3)xSO(5) normalizer.
     the_subspace = mu.numpy_from_nonzero_entries(
         [70, 5],
         [
             (1.0, 0, 0),
             (1.0, 1, 1),  # The diagonal-traceless parts.
             (1.0, 7 + su8.inv_ij_map[(0, 1)], 2),
             (1.0, 7 + su8.inv_ij_map[(0, 2)], 3),
             (1.0, 7 + su8.inv_ij_map[(1, 2)], 4)
         ])
     # Let us actually rotate around this subspace with some
     # randomly-picked generic small SO(8)-rotation.
     rotated_subspace = mu.nsum(
         'an,ba->bn', the_subspace,
         scipy.linalg.expm(
             mu.nsum('abc,a->cb', so8_algebra,
                     mu.rng(0).normal(size=(28, ), scale=0.1))))
     normalizer = algebra.get_normalizing_subalgebra(
         so8_algebra, rotated_subspace)
     self.assertEqual((28, 3 + 10),
                      normalizer.shape)  # dim(SO(3)xSO(5)) = 13.
コード例 #4
0
def get_theta_u4xr12(c=1.0):
  """Returns the Dyonic-U(4)|xR12 Theta-tensor."""
  spin8 = algebra.g.spin8
  su8 = algebra.g.su8
  theta = numpy.zeros([56, 133])
  d6 = numpy.diag([0.0] * 6 + [1.0] * 2)
  cd6 = numpy.eye(8) - d6
  theta[:28, 105:] += (-1 / 64.0) * mu.nsum(
      'Iij,Kkl,ijab,klcd,ac,bd->IK',
      su8.m_28_8_8, su8.m_28_8_8,
      spin8.gamma_vvss, spin8.gamma_vvss,
      cd6, numpy.eye(8))
  theta[28:, 105:] += (-1 / 64.0) * mu.nsum(
      'Iij,Kkl,ijab,klcd,ac,bd->IK',
      su8.m_28_8_8, su8.m_28_8_8,
      spin8.gamma_vvss, spin8.gamma_vvss,
      c * d6, numpy.eye(8))
  theta[:28, :35] += -(1 / 16.0) * mu.nsum(
      'Iij,Aab,ijcd,ac,bd->IA',
      su8.m_28_8_8,
      su8.m_35_8_8,
      spin8.gamma_vvss,
      numpy.eye(8), d6)
  theta[28:, :35] += -(1 / 16.0) * mu.nsum(
      'Iij,Aab,ijcd,ac,bd->IA',
      su8.m_28_8_8,
      su8.m_35_8_8,
      spin8.gamma_vvss,
      numpy.eye(8), c * d6)
  return theta
コード例 #5
0
 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)
コード例 #6
0
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))
コード例 #7
0
def gauge_group_gens_from_theta(theta, threshold=1e-4, e7_gens=None):
    """Determines the gauge group generators, given the Theta-tensor."""
    # First, we need to know how each of the E7-generators acts on the
    # Theta-tensor. We can express this as a (56 x 133) x 133-matrix.
    if e7_gens is None:
        e7_gens = numpy.eye(133)
    d_theta = (-mu.nsum('M^b,_aN^M,an->N^bn', theta, e7.t56r, e7_gens) +
               mu.nsum('M^b,_ab^c,an->M^cn', theta, e7.f_abC, e7_gens))
    su, ss, svh = numpy.linalg.svd(d_theta.reshape(-1, e7_gens.shape[-1]))
    del su  # Unused.
    return svh[ss <= threshold].T
コード例 #8
0
def get_boosted_theta_so8(omega, v70, e7=None):
    """Returns a boosted Theta-tensor for dyonic SO(8) gauging."""
    if e7 is None:
        e7 = algebra.g.e7
    theta = numpy.zeros([56, 133])
    cw, sw = numpy.cos(omega), numpy.sin(omega)
    theta[:28, 3 * 35:] = +cw * numpy.eye(28) * 0.25
    theta[28:, 3 * 35:] = sw * numpy.eye(28) * 0.25
    g56 = mu.expm(mu.nsum('a,_aM^N->^N_M', v70, e7.t56r[:70]))
    g133 = mu.expm(mu.nsum('a,_ab^C->^C_b', -v70, e7.f_abC[:70]))
    return mu.nsum('_M^a,^M_N,^b_a->_N^b', theta, g56, g133)
コード例 #9
0
def f_MNP_from_Theta(Theta, e7=e7, rcond=1e-15):
    """Computes the embedded gauge group structure constants."""
    X = get_X(Theta, e7=e7)
    # [X_M, X_N] = f_MN^P X_P
    kX_ab = mu.nsum('MPQ,NQP->MN', X, X)
    # kX_ab will have rank 28, not 56. So, we need to use a pseudo-inverse here.
    kX_inv_ab = numpy.linalg.pinv(kX_ab, rcond=rcond)
    X_MN = mu.asymm2(mu.nsum('MPR,NRQ->MNPQ', X, X), 'MNPQ->NMPQ')
    X_MN_P = mu.nsum('MNRS,PSR->MNP', X_MN, X)
    # Problem: kX_ab has rank < 56.
    # Let's try getting the f_MNP from solving a least-squares problem.
    f_MNP = mu.nsum('MNQ,QP->MNP', X_MN_P, kX_inv_ab)
    return f_MNP
コード例 #10
0
 def test_e7_56(self):
     """Tests the weightspace decomposition of the E7 56-irrep."""
     e7 = algebra.g.e7
     spin8 = algebra.g.spin8
     t_aMN = e7.t_a_ij_kl
     fano_mabs = numpy.stack(
         [spin8.gamma_vvvvss[ijkl] for ijkl in e7.fano_ijkl], axis=0)
     cartan7_op56_exact = mu.nsum('nsS,asS,aMN->nNM', fano_mabs,
                                  e7.v70_from_sc8x8[:, 0, :, :],
                                  t_aMN[:70, :, :]) / 8
     # Simulate numerically-noisy (with reproducible noise) Cartan generators.
     cartan7_op56 = cartan7_op56_exact + numpy.random.RandomState(0).normal(
         size=cartan7_op56_exact.shape, scale=1e-10)
     e7_56_ws = list(cartan_dynkin.get_weightspaces(cartan7_op56).items())
     self.assertEqual({w.shape for _, w in e7_56_ws}, {(1, 56)})
     weights_with_bad_eigenvalues = [
         weight for weight, _ in e7_56_ws
         if any(w not in (-0.5, 0, 0.5) for w in weight)
     ]
     self.assertEqual(weights_with_bad_eigenvalues, [])
     weights_with_bad_allocation = [
         weight for weight, _ in e7_56_ws
         if sum(w == 0 for w in weight) != 4
     ]
     self.assertEqual(weights_with_bad_allocation, [])
コード例 #11
0
 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
コード例 #12
0
  def v70o_goldstone_basis_and_projector(self, v70o, ev_threshold=1e-5):
    """Computes a basis and a projector onto the 'goldstone directions'.

    Given a vector of 70 scalar field parameters in the orthonormal basis,
    determines a basis and projector for the subspace of directions that
    we get by applying so(8) generators to the vector (again, referring
    to the orthonormal basis.)

    Args:
      v70o: Optional [70]-numpy.ndarray, the scalar field parameters
        in the orthonormal basis.
      ev_threshold: Threshold for SVD singular values.

    Returns:
      A tuple (dim_goldstone_basis, basis, goldstone_projector)
      that gives us the dimensionality D == dim_goldstone_basis
      of the vector space V spanned by applying so(8) generators
      to the input vector, plus a [70, 70]-array B that provides
      an orthonormal basis for the scalars where B[:, :D] is an
      orthonormal basis of the D-dimensional subspace V, plus
      a [70, 70]-projector matrix that performs orthogonal projection
      onto V.
    """
    e7 = self.e7
    so8_rotated_v70o = mu.nsum('abC,b->Ca',
                               e7.fo_abC[105:, :70, :70], v70o)
    svd_so8_rot_u, svd_so8_rot_s, svd_so8_rot_vh = (
        numpy.linalg.svd(so8_rotated_v70o, full_matrices=True))
    del svd_so8_rot_vh  # Unused, named for documentation only.
    num_goldstone_directions = (svd_so8_rot_s > ev_threshold).sum()
    goldstone_directions = svd_so8_rot_u[:, :num_goldstone_directions]
    proj_goldstone = goldstone_directions.dot(goldstone_directions.T)
    return num_goldstone_directions, svd_so8_rot_u, proj_goldstone
コード例 #13
0
 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
コード例 #14
0
def show_theta(Theta, e7=e7):
    """Prints a Theta-tensor and the associated gauging's Killing form."""
    f_MNP = f_MNP_from_Theta(Theta, rcond=1e-15, e7=e7)
    k_ab = mu.nsum('MPQ,NQP->MN', f_MNP, f_MNP)
    mu.tprint(Theta, d=5, name='Theta')
    mu.tprint(k_ab, d=5, name='k_MN')
    print(
        'K_ab eigvals:',
        sorted(
            collections.Counter(numpy.linalg.eigvals(k_ab).round(6)).items()))
コード例 #15
0
 def test_sl2x7(self):
     # TODO(tfish): Parametrize by Spin(8)-conventions.
     e7 = algebra.g.e7
     for scv in range(3):
         self.assertTrue(
             numpy.allclose(
                 0,
                 mu.nsum('ma,nb,abC->mnC', e7.sl2x7[scv], e7.sl2x7[scv],
                         e7.f_abC)))
     # The (s, c)-commutators must produce the v-generators.
     comms_sc = mu.nsum('ma,nb,abC->mnC', e7.sl2x7[0], e7.sl2x7[1],
                        e7.f_abC)
     d777 = numpy.zeros([7, 7, 7])  # 'diagonal-promoter'.
     for i in range(7):
         d777[i, i, i] = 1
     comms_expected = 2 * numpy.pad(
         mu.nsum('Aab,nab,AB,npq->pqB', e7.su8.m_35_8_8, e7.sl2x7_88v,
                 e7.su8.ginv35, d777), [(0, 0), (0, 0), (70, 28)])
     self.assertTrue(numpy.allclose(comms_sc, comms_expected))
コード例 #16
0
 def show_position_tex(self, position, digits=6):
   """Returns a text-string that shows the position."""
   m35s = mu.nsum('Iij,I->ij', self.e7.su8.m_35_8_8, position[:35])
   m35c = mu.nsum('Iij,I->ij', self.e7.su8.m_35_8_8, position[35:70])
   fmt_num = lambda x: f'{x:.0{digits}f}'
   def dotified_m(sc, ij, is_positive):
     sign_str = '' if is_positive else '-'
     indices = (r'\dot{}\dot{}' if sc else '{}{}').format(*ij)
     return '%sM_{%s}' % (sign_str, indices)
   pos_sign_by_text = collections.defaultdict(list)
   for sc, m35 in ((0, m35s), (1, m35c)):
     for ij in itertools.product(range(8), range(8)):
       if not ij[0] <= ij[1]:
         # We only report entries of the upper-triangular part of these
         # symmetric matrices.
         continue
       num = m35[ij]
       abs_num_str = fmt_num(abs(num))
       if float(abs_num_str) == 0:  # Skip zeroes.
         continue
       pos_sign_by_text[abs_num_str].append((sc, ij, num > 0))
   groups = sorted(
       [(sorted(locations), abs_num_str)
        for abs_num_str, locations in pos_sign_by_text.items()])
   tex_pieces = []
   for raw_locations, abs_num_str in groups:
     is_plus_first = raw_locations[0][-1]
     if is_plus_first:
       locations = raw_locations
       num_str = abs_num_str
     else:
       # 'is_plus' gets replaced by relative sign w.r.t. first such entry.
       locations = [(sc, ij, not is_plus) for sc, ij, is_plus in raw_locations]
       num_str = '-' + abs_num_str
     tex_pieces.append(
         r'$\scriptstyle %s\approx%s$' % (
             r'{\approx}'.join(dotified_m(*sc_ij_relative_sign)
                               for sc_ij_relative_sign in locations),
             num_str))
   return (r'{\begin{minipage}[t]{10cm}'
           r'\begin{flushleft}%s\end{flushleft}\end{minipage}}\\' %
           ', '.join(tex_pieces))
コード例 #17
0
 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
コード例 #18
0
 def reduce_thetas(thetas_now, e7_gen):
     # Z = batch-index.
     d_thetas = get_d_thetas(thetas_now, e7_gen)
     # We want to find those thetas that are invariant under this particular
     # rotation with some E7-element. Let us try to do this via a SVD.
     su, ss, svh = numpy.linalg.svd(d_thetas.reshape(-1, 56 * 133))
     del svh  # Unused.
     # d_thetas[k, :].reshape(...) gives us what the k-th Theta got mapped to.
     # We want to know those weight-vectors for the original Theta-s that give
     # us zeroes.
     if verbose:
         print('SVD ss:', sorted(collections.Counter(ss.round(3)).items()))
     kernel_basis = su.T[ss <= threshold]
     return mu.nsum('YZ,Z_M^b->Y_M^b', kernel_basis, thetas_now)
コード例 #19
0
 def show_position_text(self, position):
   """Returns a text-string that shows the position."""
   m288 = mu.nsum('a,axij->xij', position, self.e7.v70_as_sc8x8)
   m8x8s = m288[0].round(5)
   m8x8c = m288[1].round(5)
   def fmt8x8(m):
     # Pylint wrongly complains about `row` not being defined.
     # pylint: disable=undefined-loop-variable
     return '\n'.join(
         ', '.join('%+.5f' % x if x else ' 0      ' for x in row)
         for row in m)
     # pylint: enable=undefined-loop-variable
   return (f'=== 8x8s ===\n{fmt8x8(m8x8s)}\n'
           f'=== 8x8c ===\n{fmt8x8(m8x8c)}\n')
コード例 #20
0
def e7_rotate_theta(theta, e7_rotation, order133_c_first=True, e7=e7):
    """E7-rotates a Theta-tensor.

  Args:
    theta: [56, 133]-numpy.ndarray, the Theta-tensor to rotate.
    e7_rotation: [133]-numpy.ndarray, the e7 generator parameters
      to build the rotation from.
    order133_c_first: If true, transform the ^a index on the
      Theta-tensor via noncompact_cb compact_ba theta...^a, i.e.
      applying the compact rotation built from the su(8) part
      of e7_rotation first. If false, the noncompact rotation
      will get applied first. (Useful for applying an inverted
      rotation.)
    e7: The e7 algebra that provides the relevant definitions.

  Returns:
    A [56, 133]-numpy.ndarray, the rotated Theta-tensor.
  """
    # The 'rotation' is split here: We separately exponentiate
    # the 'compact' and 'noncompact' part, and, on an ^a-index,
    # apply noncompact @ compact.
    rot_nc133 = scipy.linalg.expm(
        mu.nsum('abC,a->Cb', e7.f_abC[:70], e7_rotation[:70]))
    rot_c133 = scipy.linalg.expm(
        mu.nsum('abC,a->Cb', e7.f_abC[70:], e7_rotation[70:]))
    rot_nc56 = scipy.linalg.expm(
        mu.nsum('aMN,a->NM', e7.t56r[:70], -e7_rotation[:70]))
    rot_c56 = scipy.linalg.expm(
        mu.nsum('aMN,a->NM', e7.t56r[70:], -e7_rotation[70:]))
    if order133_c_first:
        rot_133 = rot_nc133 @ rot_c133
        rot_56 = rot_c56 @ rot_nc56
    else:
        rot_133 = rot_c133 @ rot_nc133
        rot_56 = rot_nc56 @ rot_c56
    return mu.nsum('Ma,ba,MN->Nb', theta, rot_133, rot_56)
コード例 #21
0
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
コード例 #22
0
def get_gaugeability_condition_violations(Theta,
                                          checks=('omega', 'XX', 'linear_a',
                                                  'linear_b'),
                                          e7=e7,
                                          atol=1e-8,
                                          early_exit=True):
    """Checks whether the Theta-tensor satisfies the gaugeability constraints."""
    violations = {}
    X = get_X(Theta, e7=e7)
    if 'omega' in checks:  # (2.11) in arXiv:1112.3345
        omega_theta2 = mu.nsum('MN,Ma,Nb->ab', e7.omega, Theta, Theta)
        if not numpy.allclose(0, omega_theta2, atol=atol):
            violations['omega'] = sum(abs(omega_theta2).ravel()**2)**.5
            if early_exit:
                return violations
    if 'XX' in checks:  # (2.12) in arXiv:1112.3345
        XX_products = numpy.einsum('MPQ,NQR->MNPR', X, X)
        XX_comm = XX_products - numpy.einsum('MNPR->NMPR', XX_products)
        XX_MN = -numpy.einsum('MNP,PQR->MNQR', X, X)
        if not numpy.allclose(XX_comm, XX_MN, atol=atol):
            violations['XX'] = sum(abs(XX_comm - XX_MN).ravel()**2)**.5
            if early_exit:
                return violations
    if 'linear_a' in checks:  # (2.14) in arXiv:1112.3345
        deviation_a = numpy.einsum('Na,aMN->M', Theta, e7.t56r)
        if not numpy.allclose(0, deviation_a, atol=atol):
            violations['linear_a'] = sum(abs(deviation_a)**2)
            if early_exit:
                return violations
    if 'linear_b' in checks:
        k56_inv = numpy.linalg.inv(
            numpy.einsum('aMN,bNM->ab', e7.t56r, e7.t56r))
        tt = numpy.einsum('bMP,APN->bAMN', e7.t56r,
                          numpy.einsum('aPN,aA->APN', e7.t56r, k56_inv))
        tt_Theta = numpy.einsum('bAMN,Nb->MA', tt, Theta)
        deviation_b = tt_Theta + 0.5 * Theta
        if not numpy.allclose(0, deviation_b, atol=atol):
            violations['linear_b'] = sum(abs(deviation_b.ravel())**2)**.5
    return frozenset(violations.items())
コード例 #23
0
def get_gauging_from_theta(theta, max_residuals=1e-4):
    """Analyzes a Theta-tensor and returns the corresponding Gauging."""
    gg_gens = gauge_group_gens_from_theta(theta, threshold=0.1)
    # Inner product on the gauge group generators,
    # induced by the e7-algebra's inner product.
    k_gg = mu.nsum('am,bn,ab->mn', gg_gens, gg_gens, e7.k133)
    gg_onb, gg_onbi = mu.get_gramian_onb(k_gg, eigenvalue_threshold=1.0)
    del gg_onbi  # Unused, named for documentation only.
    gg_gens_onb = mu.nsum('am,nm->an', gg_gens, gg_onb)
    #
    # Let us check that we indeed did it right.
    k_gg_onb = mu.nsum('am,bn,ab->mn', gg_gens_onb, gg_gens_onb, e7.k133)
    diag_k_gg_onb = numpy.diag(k_gg_onb)
    assert numpy.allclose(k_gg_onb, numpy.diag(diag_k_gg_onb), atol=0.01), (
        'Inner product on the gauge group is off in the orthonormal basis')
    rel_diag_k_gg_onb = diag_k_gg_onb / abs(diag_k_gg_onb).max()
    #
    gg_null = gg_gens_onb[:, abs(rel_diag_k_gg_onb) < 0.01]
    gg_compact = gg_gens_onb[:, rel_diag_k_gg_onb < -0.01]
    gg_compact_commutators = mu.nsum('abC,am,bn->Cmn', e7.f_abC, gg_compact,
                                     gg_compact)
    (gg_compact_commutators_decomp, gg_decomp_residuals,
     *_) = numpy.linalg.lstsq(gg_compact,
                              gg_compact_commutators.reshape(133, -1),
                              rcond=1e-5)
    assert abs(gg_decomp_residuals).max() < max_residuals, (
        '[Compact, Compact] ~ Compact decomposition has large residuals.')
    dim_compact = gg_compact.shape[-1]
    f_abC_compact = gg_compact_commutators_decomp[:dim_compact].reshape(
        [dim_compact] * 3)
    k_ab_compact = mu.nsum('mbc,ncb->mn', f_abC_compact, f_abC_compact)
    gg_compact_onb, gg_compact_onbi = mu.get_gramian_onb(
        k_ab_compact, eigenvalue_threshold=0.01)
    del gg_compact_onbi  # Unused, named for documentation only.
    gg_compact_gens_onb = mu.nsum('am,nm->an', gg_compact, gg_compact_onb)
    k_ab_compact_onb = mu.nsum('Mm,Nn,mn->MN', gg_compact_onb, gg_compact_onb,
                               k_ab_compact)
    k_ab_compact_onb_diag = numpy.diag(k_ab_compact_onb)
    gg_u1s = gg_compact_gens_onb[:, abs(k_ab_compact_onb_diag) < 0.5]
    gg_compact_semisimple = gg_compact_gens_onb[:,
                                                abs(k_ab_compact_onb_diag
                                                    ) >= 0.5]
    dim_compact_semisimple = gg_compact_semisimple.shape[-1]
    random_gg_semisimple_elem = gg_compact_semisimple.dot(
        mu.rng(0).normal(size=dim_compact_semisimple))
    # Let us see what the subspace of the compact-semisimple algebra
    # looks like that commutes with this random element.
    m_commute_with_random = mu.nsum('abC,a,bn->Cn', e7.f_abC,
                                    random_gg_semisimple_elem,
                                    gg_compact_semisimple)
    # 'csa' == 'Cartan Subalgebra'
    svd_csa_u, svd_csa_s, svd_csa_vh = numpy.linalg.svd(m_commute_with_random,
                                                        full_matrices=False)
    del svd_csa_u  # Unused, named for documentation only.
    csa_basis = svd_csa_vh[svd_csa_s < 1e-5, :].T
    gg_gens = numpy.concatenate([gg_compact_semisimple, gg_u1s, gg_null],
                                axis=-1)
    gg_ranges = (0, gg_compact_semisimple.shape[1],
                 gg_compact_semisimple.shape[1] + gg_u1s.shape[1], 28)
    assert gg_gens.shape[1] == 28, 'Gauge group is not 28-dimensional.'
    return Gauging(theta=theta,
                   gg_gens=gg_gens,
                   gg_ranges=gg_ranges,
                   cartan_subalgebra=csa_basis)
コード例 #24
0
 def theta_key(theta):
     return (mu.nsum('Ma,Mb,ab->', theta, theta,
                     e7.k133), mu.nsum('Ma,Ma->', theta[28:], theta[28:]))
コード例 #25
0
 def get_d_thetas(thetas, e7_gen):
     return (-mu.nsum('Z_M^b,_aN^M,a->Z_N^b', thetas, e7.t56r, e7_gen) +
             mu.nsum('Z_M^b,_ab^c,a->Z_M^c', thetas, e7.f_abC, e7_gen))
コード例 #26
0
 def get_random_gen():
     return mu.nsum('na,n->a', e7_gens, rng.normal(size=len(e7_gens)))
コード例 #27
0
def get_invariant_thetas(
        e7_gens,
        seed=0,
        num_rounds_max=100,
        threshold=1e-4,
        thetas_start=None,
        filename=None,
        onb_form=True,  # Whether to transform to ONB.
        e7=e7,  # The E7 algebra to use.
        verbose=True):
    """Computes a basis for e7_gens-invariant Thetas in the 912-irrep."""
    # Index structure: Theta_M^a, with M a e7-real index;
    # returns a [N, 56, 133]-array.
    if filename is not None:
        try:
            return numpy.load(filename)
        except (IOError, OSError):
            pass
    # If we reach this point, loading cached thetas did not work.
    t0 = time.time()
    if verbose:
        print('Computing thetas.')
    rng = numpy.random.RandomState(seed=seed)

    def get_random_gen():
        return mu.nsum('na,n->a', e7_gens, rng.normal(size=len(e7_gens)))

    def get_d_thetas(thetas, e7_gen):
        return (-mu.nsum('Z_M^b,_aN^M,a->Z_N^b', thetas, e7.t56r, e7_gen) +
                mu.nsum('Z_M^b,_ab^c,a->Z_M^c', thetas, e7.f_abC, e7_gen))

    def reduce_thetas(thetas_now, e7_gen):
        # Z = batch-index.
        d_thetas = get_d_thetas(thetas_now, e7_gen)
        # We want to find those thetas that are invariant under this particular
        # rotation with some E7-element. Let us try to do this via a SVD.
        su, ss, svh = numpy.linalg.svd(d_thetas.reshape(-1, 56 * 133))
        del svh  # Unused.
        # d_thetas[k, :].reshape(...) gives us what the k-th Theta got mapped to.
        # We want to know those weight-vectors for the original Theta-s that give
        # us zeroes.
        if verbose:
            print('SVD ss:', sorted(collections.Counter(ss.round(3)).items()))
        kernel_basis = su.T[ss <= threshold]
        return mu.nsum('YZ,Z_M^b->Y_M^b', kernel_basis, thetas_now)

    #
    thetas_now = (thetas_start if thetas_start is not None else numpy.eye(
        56 * 133).reshape(-1, 56, 133))  # A 7448 x 7448 matrix!
    prev_num_thetas = -1  # Impossible int at start.
    for num_round in range(num_rounds_max):
        thetas_now = reduce_thetas(thetas_now, get_random_gen())
        num_thetas = thetas_now.shape[0]
        if verbose:
            print(f'Round {num_round}, num_thetas={num_thetas}')
        if num_thetas == prev_num_thetas:
            break
        prev_num_thetas = num_thetas
    gramian = mu.nsum('ZMa,WMb,ab->ZW', thetas_now, thetas_now, e7.k133)
    if verbose:
        t1 = time.time()
        print('Done computing thetas, T=%.3f sec' % (t1 - t0))
    if not onb_form:
        return thetas_now, gramian
    # Need to bring to ONB-form.
    onb, onbi = mu.get_gramian_onb(gramian)
    del onbi  # Unused.
    thetas_onb = mu.nsum('ZMa,WZ->WMa', thetas_now, onb)
    gramian_onb = mu.nsum('ZMa,WMb,ab->ZW', thetas_onb, thetas_onb, e7.k133)
    assert numpy.allclose(gramian_onb, numpy.diag(numpy.diag(gramian_onb)))
    if filename is not None:
        numpy.savez_compressed(filename, thetas_now, gramian_onb)
    return thetas_onb, gramian_onb