def coxeter(self): r""" Returns a list expressing the coxeter element corresponding to self._B (twisted) reflections are applied from top of the list, for example [2, 1, 0] correspond to s_2s_1s_0 Sources == non positive columns == leftmost letters """ zero_vector = vector([0 for x in range(self.rk)]) coxeter = [] B = copy(self.B0) columns = B.columns() source = None for j in range(self.rk): for i in range(self.rk): if all(x <=0 for x in columns[i]) and columns[i] != zero_vector: source = i break if source == None: if B != matrix(self.rk): raise ValueError("Unable to find a Coxeter element representing self.B0") coxeter += [ x for x in range(self.rk) if x not in coxeter] break coxeter.append(source) columns[source] = zero_vector B = matrix(columns).transpose() B[source] = zero_vector columns = B.columns() source = None return tuple(coxeter)
def run_example(filepath,more_info=False): e = E.Example(filepath) mA = S.matrix(S.QQ,lmatrix_to_numbers(e.matrix_A)) mB_s = S.matrix(S.QQ,lmatrix_to_numbers(e.matrix_B_strict)) mB_w = S.matrix(S.QQ,lmatrix_to_numbers(e.matrix_B_weak)) print "Checking termination for example '"+ e.example_name +"'" print "published in " + e.published_in if more_info: print "Matrix A:" print mA print "Matrix jnf(A):" print mA.jordan_form(S.QQbar) print "Matrix B strict:" print mB_s print "Matrix B weak:" print mB_w print ("applicable to complexity theorem: " + str(C.are_absolute_eigenvalues_of_jordan_blocks_distinct(mA))) print ("polynomial update: " + str(C.has_polynomial_growth(mA))) print ("max growth: " + str(C.pretty_growth(C.max_growth(mA)))) sys.stdout.flush() start_time = time.time() result = T.termination_check(mA,mB_s,mB_w,more_info) end_time = time.time() print "result:",result print ("time: %0.4f seconds" % (end_time - start_time)) print "-"*40+"\n" sys.stdout.flush()
def compatibility_degree(self, alpha, beta): if self.is_finite(): tube_contribution = -1 elif self.is_affine(): gck = self.gamma().associated_coroot() if any([gck.scalar(alpha) != 0, gck.scalar(beta) != 0]): tube_contribution = -1 else: sup_a = self._tube_support(alpha) sup_b = self._tube_support(beta) if all([x in sup_b for x in sup_a]) or all([x in sup_a for x in sup_b]): tube_contribution = -1 else: nbh_a = self._tube_nbh(alpha) tube_contribution = len([ x for x in nbh_a if x in sup_b ]) else: raise ValueError("compatibility degree is implemented only for finite and affine types") initial = self.initial_cluster() if alpha in initial: return max(beta[initial.index(alpha)],0) alphacheck = alpha.associated_coroot() if beta in initial: return max(alphacheck[initial.index(beta)],0) Ap = -matrix(self.rk, map(lambda x: max(x,0), self.b_matrix().list() ) ) Am = matrix(self.rk, map(lambda x: min(x,0), self.b_matrix().list() ) ) a = vector(alphacheck) b = vector(beta) return max( -a*b-a*Am*b, -a*b-a*Ap*b, tube_contribution )
def _load_bp(self, fname): bp = [] try: with open(fname) as f: for line in f: if line.startswith('#'): continue bp_json = json.loads(line) for step in bp_json['steps']: bp.append( Layer(int(step['position']), matrix(step['0']), matrix(step['1']))) assert len(bp_json['outputs']) == 1 and \ len(bp_json['outputs'][0]) == 2 first_out = bp_json['outputs'][0][0].lower() if first_out not in ['false', '0']: if first_out not in ['true', '1']: print('warning: interpreting %s as a truthy output' % first_out) bp[-1].zero.swap_columns(0,1) bp[-1].one .swap_columns(0,1) return bp except IOError as e: print(e) sys.exit(1) except ValueError as e: print('expected numeric position while parsing branching program JSON') print(e) sys.exit(1)
def kernel_lattice(A, mod=None): ''' Lattice of vectors x with Ax = 0 (potentially mod m) ''' A = matrix(ZZ if mod is None else Integers(mod), A) L = [vector(ZZ, row) for row in A.right_kernel().basis()] if mod is not None: cols = len(L[0]) for i in range(cols): L.append([0]*i + [mod] + [0]*(cols-i-1)) return matrix(L)
def _rankin_cohen_triple_det_sym2_pol(k1, k2, k3): (r11, r12, r22, s11, s12, s22, t11, t12, t22), (u1, u2) = _triple_gens() m0 = matrix([[r11, s11, t11], [2 * r12, 2 * s12, 2 * t12], [k1, k2, k3]]) m1 = matrix([[r11, s11, t11], [k1, k2, k3], [r22, s22, t22]]) m2 = matrix([[k1, k2, k3], [2 * r12, 2 * s12, 2 * t12], [r22, s22, t22]]) Q = m0.det() * u1**2 - 2 * m1.det() * u1 * u2 + m2.det() * u2**2 return Q
def _blocks_to_quad_form(blcs, p): h = matrix([[QQ(0), QQ(1) / QQ(2)], [QQ(1) / QQ(2), QQ(0)]]) y = matrix([[QQ(1), QQ(1) / QQ(2)], [QQ(1) / QQ(2), QQ(1)]]) mat_dict = {"h": h, "y": y} mats_w_expt = [(expt, mat_dict[qf] if qf in ("h", "y") else matrix([[qf]])) for expt, qf in blcs] qfs = [QuadraticForm(ZZ, m * ZZ(2) * p ** expt) for expt, m in mats_w_expt] return reduce(operator.add, qfs)
def matrix_representaion(self, lin_op): '''Let lin_op(f, t) be an endomorphsim of self, where f is a modular form and t is a object corresponding to a matrix. This medthod returns the matrix representation of lin_op. ''' basis = self.basis() lin_indep_tuples = self.linearly_indep_tuples() m1 = matrix([[f[t] for t in lin_indep_tuples] for f in basis]) m2 = matrix([[lin_op(f, t) for t in lin_indep_tuples] for f in basis]) return (m2 * m1 ** (-1)).transpose()
def cvp_embed(L, v, b=None): if not b: b = max(max(row) for row in L.rows()) L2 = matrix([list(row) + [0] for row in L] + [list(v) + [b]]) res = None for x in lll(matrix(L2)): if x[-1] > 0: x = -x if x[-1] == -b: u = vector(x[:-1]) + v assert in_lattice(L, u) if res is None or (v - u).norm() < (v - res).norm(): res = u return res
def test_division_generators(self): prec = 6 div_consts = [c for c in gens_consts if isinstance(c, ConstDivision)] consts = (even_gen_consts() + odd_gen_consts() + [CVH(_wt18_consts[0], 2), CVH(sym10_19_consts[0], 2)]) calculator = CalculatorVectValued(consts, data_dir) calculator.calc_forms_and_save(prec, verbose=True, force=True) gens_dct = calculator.forms_dict(prec) for c in div_consts: k = c.weight() print "checking when k = %s" % (str(k), ) if k % 2 == 0: sccst = _find_const_of_e4_e6_of_same_wt(18 - k) M = Sym10EvenDiv(sccst, prec) else: sccst = _find_const_of_e4_e6_of_same_wt(19 - k) M = Sym10OddDiv(sccst, prec) pl = _anihilate_pol(k, M) hol_basis = M.basis_of_subsp_annihilated_by(pl) N = Sym10GivenWtBase(prec, k, hol_basis) # Check this prec is sufficient. mt = matrix(QQ, [[b[t] for b in N.basis()] for t in N.linearly_indep_tuples()]) self.assertTrue( mt.is_invertible(), "False when k = %s" % (str(k),)) # Check our construction gives a holomorphic modular form self.assertTrue(N.contains(gens_dct[c]), "False when k = %s" % (str(k),))
def gamma(self): """r Assume roots are labeled by range(self._n) Return the generalized eigenvector of the cartan matrix of eigenvalue 1 in the span of the finite root system """ if self.is_affine(): C = [self.c(x) for x in self.simple_roots()] C = map(vector, C) C = matrix(C).transpose() delta = vector(self.delta()) gamma = (C - 1).solve_right(delta) # the following two lines could probably ve replaced by the # assumption i0 = 0 ct = self.cartan_type() i0 = [ i for i in ct.index_set() if i not in ct.classical().index_set() ][0] gamma = gamma - gamma[i0] / delta[i0] * delta gamma = sum( [gamma[i] * self.simple_roots()[i] for i in range(self.rk)]) return gamma else: raise ValueError("gamma is defined only for affine types")
def is_independent(self, v): """ Return True if the Hecke operators in v are independent. INPUT: - `v` -- four elements of the Hecke algebra mod 2 (represented as matrices) OUTPUT: - bool EXAMPLES:: sage: from mdsage import * sage: C = KamiennyCriterion(29) sage: C.is_independent([C.T(1), C.T(2), C.T(3), C.T(4)]) True sage: C.is_independent([C.T(1), C.T(2), C.T(3), C.T(1)+C.T(3)]) False """ # X = matrix(GF(2), 4, sum([a.list() for a in v], [])) # c = sage.matrix.matrix_modn_dense.Matrix_modn_dense(X.parent(),X.list(),False,True) # return c.rank() == 4 # This crashes! See http://trac.sagemath.org/sage_trac/ticket/8301 return matrix(GF(2), len(v), sum([a.list() for a in v], [])).rank() == len(v) raise NotImplementedError
def linear_dependencies(m, err_dec=20, round_dec=10): import numpy as np from sage.all import matrix, round W = m.numpy() (nr, nc) = W.shape d = np.identity(nc) if not W[:, 0].any(): d[0, 0] = 0.0 for i in range(1, nc): if not W[:, i].any(): d[i, i] = 0.0 else: A = W[:, :i] b = W[:, i:i + 1] Api = np.linalg.pinv(A) x = np.dot(Api, b) err = np.linalg.norm(b - np.dot(A, x)) if err < 10.0**(-err_dec): d[:, i:i + 1] = np.vstack( (x.reshape(i, 1), np.zeros((nc - i, 1)))) W[:, i:i + 1] = np.zeros((nr, 1)) return matrix(d).apply_map(lambda i: round(i, round_dec))
def factorize_msb(moduli, bitsize, shared_bitsize): """ Factorizes the moduli when some most significant bits are equal among multiples of a prime factor. More information: Nitaj A., Ariffin MRK., "Implicit factorization of unbalanced RSA moduli" (Section 4) :param moduli: the moduli :param bitsize: the amount of bits of the moduli :param shared_bitsize: the amount of shared most significant bits :return: a list containing a tuple of the factors of each modulus, or None if the factors were not found """ L = matrix(ZZ, len(moduli), len(moduli)) L[0, 0] = 2**(bitsize - shared_bitsize) for i in range(1, len(moduli)): L[0, i] = moduli[i] for i in range(1, len(moduli)): L[i, i] = -moduli[0] L = L.LLL() for row in range(L.nrows()): factors = [] for col in range(L.ncols()): modulus = moduli[col] q = gcd(L[row, col], modulus) if 1 < q < modulus and modulus % q == 0: factors.append((modulus // q, q)) if len(factors) == len(moduli): return factors
def lattice_search_isometric(res, info, query): """ We check for isometric lattices if the user enters a valid gram matrix but not one stored in the database This may become slow in the future: at the moment we compare against a list of stored matrices with same dimension and determinant (just compare with respect to dimension is slow) """ if info['number'] == 0 and info.get('gram'): A = query['gram'] n = len(A[0]) d = matrix(A).determinant() for gram in db.lat_lattices.search({'dim': n, 'det': int(d)}, 'gram'): if isom(A, gram): query['gram'] = gram proj = lattice_search_projection count = parse_count(info) start = parse_start(info) res = db.lat_lattices.search(query, proj, limit=count, offset=start, info=info) break for v in res: v['min'] = v.pop('minimum') return res
def _arc(p,q,s,**kwds): #rewrite this to use polar_plot and get points to do filled triangles from sage.misc.functional import det from sage.plot.line import line from sage.misc.functional import norm from sage.symbolic.all import pi from sage.plot.arc import arc p,q,s = map( lambda x: vector(x), [p,q,s]) # to avoid running into division by 0 we set to be colinear vectors that are # almost colinear if abs(det(matrix([p-s,q-s])))<0.01: return line((p,q),**kwds) (cx,cy)=var('cx','cy') equations=[ 2*cx*(s[0]-p[0])+2*cy*(s[1]-p[1]) == s[0]**2+s[1]**2-p[0]**2-p[1]**2, 2*cx*(s[0]-q[0])+2*cy*(s[1]-q[1]) == s[0]**2+s[1]**2-q[0]**2-q[1]**2 ] c = vector( [solve( equations, (cx,cy), solution_dict=True )[0][i] for i in [cx,cy]] ) r = norm(p-c) a_p, a_q, a_s = map(lambda x: atan2(x[1],x[0]), [p-c,q-c,s-c]) a_p, a_q = sorted([a_p,a_q]) if a_s < a_p or a_s > a_q: return arc( c, r, angle=a_q, sector=(0,2*pi-a_q+a_p), **kwds) return arc( c, r, angle=a_p, sector=(0,a_q-a_p), **kwds)
def jacobian(self): s = len(self.mcomplex.Edges) result = matrix(self.vertex_gram_matrices[0].base_ring(), s, s) for tet in self.mcomplex.Tetrahedra: cofactor_matrices = _cofactor_matrices_for_submatrices( self.vertex_gram_matrices[tet.Index]) vga = self.vertex_gram_adjoints[tet.Index] for angle_edge, (i, j) in _OneSubsimplicesWithVertexIndices: cii = vga[i, i] cij = vga[i, j] cjj = vga[j, j] dcij = -1 / sqrt(cii * cjj - cij**2) tmp = -dcij * cij / 2 dcii = tmp / cii dcjj = tmp / cjj a = tet.Class[t3m.comp(angle_edge)].Index for length_edge, (m, n) in _OneSubsimplicesWithVertexIndices: l = tet.Class[length_edge].Index result[a, l] += ( dcij * _cofactor_derivative(cofactor_matrices, i, j, m, n) + dcii * _cofactor_derivative(cofactor_matrices, i, i, m, n) + dcjj * _cofactor_derivative(cofactor_matrices, j, j, m, n)) return result
def gimbal_derivative(self): self.edge_index_to_column_index = { e: i for i, e in enumerate(self.approx_edges) } RIF = self.hyperbolic_structure.vertex_gram_matrices[0].base_ring() num_rows = 3 * len(self.mcomplex.Vertices) num_cols = len(self.approx_edges) result = matrix(RIF, num_rows, num_cols) for i, gimbal_loop in enumerate(self.gimbal_loops): path_matrices = [ self.hyperbolic_structure.so3_matrix_for_path(edgePath) for edgeLoop, edgePath in gimbal_loop ] for j, (edgeLoop, edgePath) in enumerate(gimbal_loop): col = self.edge_index_to_column_index[edgeLoop.edge_index] m = self._gimbal_derivative_matrix(gimbal_loop, path_matrices, j) result[3 * i, col] += m[0, 1] result[3 * i + 1, col] += m[0, 2] result[3 * i + 2, col] += m[1, 2] return result
def __init__(self, tiling_engine, bits_prec=2 * 53): self.RIF = RealIntervalField(bits_prec) self.CIF = ComplexIntervalField(bits_prec) self.baseTetInCenter = vector( self.RIF, complex_and_height_to_R13_time_vector( tiling_engine.baseTetInCenter.z, tiling_engine.baseTetInCenter.t)) self.generator_matrices = { g: matrix(self.RIF, PSL2C_to_O13(m)) for g, m in tiling_engine.mcomplex.GeneratorMatrices.items() } self.max_values = {} for tile in tiling_engine.all_tiles(): if tile.word: m = prod(self.generator_matrices[g] for g in tile.word) tileCenter = vector( self.RIF, complex_and_height_to_R13_time_vector( tile.center.z, tile.center.t)) #print("=====") #print(tileCenter) #print(m * self.baseTetInCenter) #print(inner_prod(m * self.baseTetInCenter, tileCenter).endpoints()) err = my_dist(m * self.baseTetInCenter, tileCenter).upper() l = len(tile.word) self.max_values[l] = max(err, self.max_values.get(l, err))
def _arc(p, q, s, **kwds): #rewrite this to use polar_plot and get points to do filled triangles from sage.misc.functional import det from sage.plot.line import line from sage.misc.functional import norm from sage.symbolic.all import pi from sage.plot.arc import arc p, q, s = map(lambda x: vector(x), [p, q, s]) # to avoid running into division by 0 we set to be colinear vectors that are # almost colinear if abs(det(matrix([p - s, q - s]))) < 0.01: return line((p, q), **kwds) (cx, cy) = var('cx', 'cy') equations = [ 2 * cx * (s[0] - p[0]) + 2 * cy * (s[1] - p[1]) == s[0]**2 + s[1]**2 - p[0]**2 - p[1]**2, 2 * cx * (s[0] - q[0]) + 2 * cy * (s[1] - q[1]) == s[0]**2 + s[1]**2 - q[0]**2 - q[1]**2 ] c = vector([ solve(equations, (cx, cy), solution_dict=True)[0][i] for i in [cx, cy] ]) r = norm(p - c) a_p, a_q, a_s = map(lambda x: atan2(x[1], x[0]), [p - c, q - c, s - c]) a_p, a_q = sorted([a_p, a_q]) if a_s < a_p or a_s > a_q: return arc(c, r, angle=a_q, sector=(0, 2 * pi - a_q + a_p), **kwds) return arc(c, r, angle=a_p, sector=(0, a_q - a_p), **kwds)
def _recover_increment_and_states(outputs, state_bitsize, output_bitsize, modulus, multiplier): B = 2**(state_bitsize - output_bitsize) mult1 = multiplier mult2 = 1 # Adapted from the code to solve the Hidden Number Problem using a lattice attack. m = len(outputs) M = matrix(QQ, m + 3, m + 3) for i in range(m): M[i, i] = modulus M[m, i] = mult1 M[m + 1, i] = mult2 # Adding B // 2 improves the quality of the results. M[m + 2, i] = (-(B * outputs[i] + B // 2)) % modulus mult1 = (multiplier * mult1) % modulus mult2 = (multiplier * mult2 + 1) % modulus M[m, m] = B / QQ(modulus) M[m + 1, m + 1] = B / QQ(modulus) M[m + 2, m] = 0 M[m + 2, m + 1] = 0 M[m + 2, m + 2] = B L = M.LLL() for row in L.rows(): seed = (int(row[m] * modulus) // B) % modulus increment = (int(row[m + 1] * modulus) // B) % modulus if seed != 0 and increment != 0 and row[m + 2] == B: states = [] for i in range(len(outputs)): states.append((B * outputs[i] + B // 2 + int(row[i]))) yield modulus, multiplier, increment, states break
def check_add_qexp(dim, min_det=1, max_det=None, fix=False): count = 0 query = {} query['dim'] = int(dim) query['det'] = {'$gte': int(min_det)} if max_det: query['det']['$lte'] = int(max_det) else: max_det = "infinity" lat_set = lat.find(query) print( "%s lattices to examine of dimension %s and determinant between %s and %s." % (lat_set.count(), dim, min_det, max_det)) if lat_set.count() == 0: return None print("checking wheter the q expansion is stored...") for l in lat_set: print("Testing lattice %s" % l['label']) if l['theta_series'] == "": print("q expansion NOT stored") if fix: M = l['gram'] exp = [ int(i) for i in gp("Vec(1+2*'x*Ser(qfrep(" + str(gp(matrix(M))) + ",150,0)))") ] lat.update({'theta_series': l['theta_series']}, {"$set": { 'theta_series': exp }}, upsert=True) print("Fixed lattice %s" % l['label']) else: print("q expansion stored")
def mod_right_kernel(A, mod): # from https://ask.sagemath.org/question/33890/how-to-find-kernel-of-a-matrix-in-mathbbzn/ # too slow though Zn = ZZ**A.ncols() M = Zn/(mod*Zn) phi = M.hom([M(a) for a in A] + [M(0) for _ in range(A.ncols()-A.nrows())]) return matrix([M(b) for b in phi.kernel().gens()])
def characteristic_polynomial(self, f, g): r""" Return the characteristic polynomial of an element of a simple extension of `K`. INPUT: - ``f``, ``g`` -- univariate monic polynomials over `K` OUTPUT: the characteristic polynomial of the element `\beta=g(\alpha)` with respect to the relative extension `L=K[\alpha]/K`, where `\alpha` is a formal root of `f`. Of course, `L` is a field only if `f` i irreducible, but this is not checked, and no error would occur within this function if `f` is reducible. """ # construct the matrix representing alpha as endo of L/K K = self.number_field() m = f.degree() A = matrix(K, m) for i in range(m - 1): A[i + 1, i] = K(1) for i in range(m): A[i, m - 1] = -f[i] B = g(A) # now B represents beta return B.charpoly()
def pi(z): # Note that f(...) returns a string right now, since it's all done at C level. # This will prboably change, breaking this code. M = matrix(k,2,eval(f(z).replace(';',','), {'g':g})).transpose() v = M.echelon_form()[0] v.set_immutable() return v
def set_peripheral_info(self): G = self.manifold.fundamental_group() phi = MapToFreeAbelianization(G) m, l = [phi(w)[0] for w in G.peripheral_curves()[0]] if m < 0: m, l = -m, -l self.m_abelian, self.l_abelian = m, l # Same as index of homological meridian in H_1(M)_free self.l_order = gcd(m, l) # We also want to be able to view things from a more homologically # natural point of view. hom_l = self.manifold.homological_longitude() if abs(hom_l[1]) == 1: hom_m = (1, 0) else: a, b = xgcd(*hom_l)[1:] hom_m = (b, -a) M = self.manifold.copy() M.set_peripheral_curves([hom_m, hom_l]) cusp = M.cusp_info(0).shape assert abs(1 + cusp) > 1 - 1e-10 and abs(1 - cusp) > 1 - 1e-10 self.hom_m = hom_m self.hom_l = hom_l self.hom_m_abelian = abs(self.m_abelian * hom_m[0] + self.l_abelian * hom_m[1]) self.change_trans_to_hom_framing = matrix([hom_m, hom_l]) # Moreover, we store two special copies of the meridian for # later use. self.special_meridians = meridians_fixing_infinity_and_zero( self.manifold)
def _rankin_cohen_triple_det_sym8_pol(k1, k2, k3): (r11, r12, r22, s11, s12, s22, t11, t12, t22), (u1, u2) = _triple_gens() def _mat_det(l): return matrix([[r11, s11, t11], [r12, s12, t12], l + [2 * k3]]).det() ls = [[2 * k1 + 6, 2 * k2], [2 * k1 + 4, 2 * k2 + 2], [2 * k1 + 2, 2 * k2 + 4], [2 * k1, 2 * k2 + 6]] coeffs = [(2 * k2 + 2) * (2 * k2 + 4) * (2 * k2 + 6) * r11 ** 3, -3 * (2 * k1 + 6) * (2 * k2 + 4) * (2 * k2 + 6) * r11 ** 2 * s11, 3 * (2 * k1 + 4) * (2 * k1 + 6) * (2 * k2 + 6) * r11 * s11 ** 2, -(2 * k1 + 2) * (2 * k1 + 4) * (2 * k1 + 6) * s11 ** 3] Q0 = sum([c * _mat_det(l) for c, l in zip(coeffs, ls)]) A = matrix([[1, u1], [0, 1]]) def bracketA(a, b, c): R = matrix([[a, b], [b, c]]) a1, b1, _, c1 = (A * R * A.transpose()).list() return (a1, b1, c1) def _subs_dct(rs): return {a: b for a, b in zip(rs, bracketA(*rs))} subs_dct = {} for rs in [[r11, r12, r22], [s11, s12, s22], [t11, t12, t22]]: subs_dct.update(_subs_dct(rs)) Q0_subs = Q0.subs(subs_dct) return sum([Q0_subs[(i, 0)] * u1 ** (8 - i) * u2 ** i for i in range(9)])
def isom(A, B): # First check that A is a symmetric matrix. if not matrix(A).is_symmetric(): return False # Then check A against the viable database candidates. else: n = len(A[0]) m = len(B[0]) Avec = [] Bvec = [] for i in range(n): for j in range(i, n): if i == j: Avec += [A[i][j]] else: Avec += [2 * A[i][j]] for i in range(m): for j in range(i, m): if i == j: Bvec += [B[i][j]] else: Bvec += [2 * B[i][j]] Aquad = QuadraticForm(ZZ, len(A[0]), Avec) # check positive definite if Aquad.is_positive_definite(): Bquad = QuadraticForm(ZZ, len(B[0]), Bvec) return Aquad.is_globally_equivalent_to(Bquad) else: return False
def resolution_graph_of_cyclic_quotient_singularity(n, r): r""" Return the resolution graph of the tame cyclic quotient singularity of type `(n,r)`. INPUT: - ``n``, ``r`` -- positive integers such that `n>r` OUTPUT: the resolution graph of a tame cyclic quotient singularity of type `(n,r)`. See .. Let `(a_0,\ldots,a_s)` be the modified continued fraction expansion of `n/r`. Then the resolution graph is a chain of components `E_0,\ldots,E_s`, where `E_i` is smooth of genus zero and has selfintersection `-a_i`. """ a = modified_continued_fraction(n, r) n = len(a) M = matrix(ZZ, n, n) for i in range(n): M[i, i] = - a[i] if i > 0: M[i, i - 1] = 1 M[i - 1, i] = 1 return ResolutionGraph(M)
def __init__(self, data): for elt in db.hgm_families.col_type: if elt not in data: data[elt] = None self.__dict__.update(data) self.alpha = cyc_to_QZ(self.A) self.beta = cyc_to_QZ(self.B) self.hodge = data['famhodge'] self.bezout = matrix(self.bezout) self.hinf = matrix(self.hinf) self.h0 = matrix(self.h0) self.h1 = matrix(self.h1) #FIXME self.rotation_number = self.imprim
def sqcap_mul(A, B, n, p, q): ''' Let A and B be square matrices of size binomial(n, p) and binomial(n, q). Return sqcap multiplication defined in [Bö] as a square matrix of size binomial(n, p + q). ''' # if p or q is zero, return immediately. if p == 0: return B elif q == 0: return A p_dct = _index_dct(n, p) q_dct = _index_dct(n, q) p_q_dct = _index_dct(n, p + q) p_q_lst = permutations_increasing(n, p + q) res = matrix([[A.base_ring()(0) for _ in p_q_lst] for _ in p_q_lst]) for ad in _permutations_increasing(n, p): for bd in _permutations_increasing(n, p): for add in _permutations_increasing(n, q): for bdd in _permutations_increasing(n, q): if all(a not in add for a in ad) and all(b not in bdd for b in bd): a = _concat(ad, add) b = _concat(bd, bdd) s = (_sign(ad, add) * _sign(bd, bdd) * A[p_dct[bd], p_dct[ad]] * B[q_dct[bdd], q_dct[add]]) res[p_q_dct[b], p_q_dct[a]] += s return binomial(p + q, p) ** (-1) * res
def assert_degree_2(self, k): es = eisenstein_series_degree2(k, prec=10) es1 = sess(weight=k, degree=2) self.assertTrue( all(es[(n, r, m)] == es1.fourier_coefficient(matrix([[n, ZZ(r) / ZZ(2)], [ZZ(r) / ZZ(2), m]])) for n, r, m in es.prec))
def kalmanson_matrix(n, aug=False): r,c = triu_indices(n, 1) k = len(r) inds = np.arange(k) row_ind = lambda (i,j): inds[np.logical_and(r==i-1, c==j-1)] upright = lambda (i,j): (i,j) if i<j else (j,i) get_ind = lambda ind_lst: np.array(map(row_ind, map(upright, grouper(2, ind_lst)))) rows = [] for i in range(1,n-2): for j in range(i+2, n): ind_lst = (i, j+1, i+1, j, i, j, i+1, j+1) indices = get_ind(ind_lst) row = np.zeros(k, dtype=np.int) row[indices] = [-1, -1, 1, 1] rows.append(row) sub = len(rows) for i in range(2, n-1): ind_lst = (i, 1, i+1, n, i, n, i+1, 1) indices = get_ind(ind_lst) row = np.zeros(k, dtype=np.int) row[indices] = [-1, -1, 1, 1] rows.append(row) mat = matrix(np.vstack(rows)) mat.subdivide(sub, None) if aug: return zero_matrix(len(rows),1).augment(mat) else: return mat
def check_add_qexp(dim, min_det=1, max_det=None, fix=False): count = 0 query = {} query['dim'] = int(dim) query['det'] = {'$gte' : int(min_det)} if max_det: query['det']['$lte'] = int(max_det) else: max_det = "infinity" lat_set = lat.find(query) print("%s lattices to examine of dimension %s and determinant between %s and %s." % (lat_set.count(), dim, min_det, max_det)) if lat_set.count() == 0: return None print("checking whether the q expansion is stored...") for l in lat_set: print("Testing lattice %s" % l['label']) if l['theta_series'] == "": print("q expansion NOT stored") if fix: M=l['gram'] exp=[int(i) for i in gp("Vec(1+2*'x*Ser(qfrep("+str(gp(matrix(M)))+",150,0)))")] lat.update({'theta_series': l['theta_series']}, {"$set": {'theta_series': exp}}, upsert=True) print("Fixed lattice %s" % l['label']) else: print("q expansion stored")
def do_import(ll): dim,det,level,gram,density,hermite,minimum,kissing,shortest,aut,theta_series,class_number,genus_reps,name,comments = ll mykeys = ['dim','det','level','gram','density','hermite', 'minimum','kissing','shortest','aut','theta_series','class_number','genus_reps','name','comments'] data = {} for j in range(len(mykeys)): data[mykeys[j]] = ll[j] blabel = base_label(data['dim'],data['det'],data['level'], data['class_number']) data['base_label'] = blabel data['index'] = label_lookup(blabel) label= last_label(blabel, data['index']) data['label'] = label lattice = lat.find_one({'label': label}) if lattice is None: print "new lattice" print "***********" print "check for isometries..." A=data['gram']; n=len(A[0]) d=matrix(A).determinant() result=[B for B in lat.find({'dim': int(n), 'det' : int(d)}) if isom(A, B['gram'])] if len(result)>0: print "... the lattice with base label "+ blabel + " is isometric to " + str(result[0]['gram']) print "***********" else: lattice = data else: print "lattice already in the database" lattice.update(data) if saving: lat.update({'label': label} , {"$set": lattice}, upsert=True)
def hom_module_induced_morphism(f, N, action, hom_M_prime_N_data, hom_M_N_data): M_prime = f.domain() M = f.codomain() hom_M_prime_N, M_prime_basis, M_prime_basis_keys, prime_components, prime_from_components, prime_hom_action = hom_M_prime_N_data hom_M_N, M_basis, M_basis_keys, components, from_components, hom_action = hom_M_N_data f_values = {} for i_prime in M_prime_basis_keys: f_values[i_prime] = f(M_prime_basis[i_prime]) def f_star_components(x): c_x = components(x) c = {} for i_prime in M_prime_basis_keys: a = f_values[i_prime] for c in N.coordinates( sum_in_module(N, [ linearly_extended_action(N, action, a[i], c_x[i]) for i in a.support() ])): yield c matrix_representing_f_star = matrix( [tuple(f_star_components(x)) for x in hom_M_N.basis()], ncols=hom_M_prime_N.rank()) return FreeModuleMorphism(Hom(hom_M_N, hom_M_prime_N), matrix_representing_f_star)
def isom(A,B): # First check that A is a symmetric matrix. if not matrix(A).is_symmetric(): return False # Then check A against the viable database candidates. else: n=len(A[0]) m=len(B[0]) Avec=[] Bvec=[] for i in range(n): for j in range(i,n): if i==j: Avec+=[A[i][j]] else: Avec+=[2*A[i][j]] for i in range(m): for j in range(i,m): if i==j: Bvec+=[B[i][j]] else: Bvec+=[2*B[i][j]] Aquad=QuadraticForm(ZZ,len(A[0]),Avec) # check positive definite if Aquad.is_positive_definite(): Bquad=QuadraticForm(ZZ,len(B[0]),Bvec) return Aquad.is_globally_equivalent_to(Bquad) else: return False
def small_lgs2(A, c, mod, sol=None): ''' Find short x with Ax = c (mod m). This is more generic because it also works for composite m, but it does not work for c = 0. The difference is that we don't have to compute the orthogonal lattice of A. TODO: For c = 0, we can brute force a k with small ||k|| and solve for c = k*mod instead. See [2] (p. 264-268) and https://crypto.stackexchange.com/questions/37836/ ''' m, n = A.dimensions() A = list(A) for i in range(n): A.append([0]*i + [mod] + [0]*(n-i-1)) A = matrix(ZZ, A) L = A.LLL() for i in range(m): assert L[i] == 0 L = L[m:] if c == 0 or c == None: # Brutal heuristic here, see TODO above. We are only trying one single k # here, namely (0, ..., 0, 1) x = normalize(integer_lgs(L, vector([0]*(n-1) + [mod]))) for t in x: assert 0 <= x < mod return x # compute Y such that Y*A == L Y = [] smith = A.transpose().smith_form() for i in range(L.nrows()): y = integer_lgs(None, L[i], smith) # assert At*y == L[i] Y.append(y) Y = matrix(ZZ, Y) # assert Y*A == L c = Y*vector(list(c)+[0]*n)%mod for i in range(len(c)): if c[i] * 2 >= mod: c[i] -= mod assert abs(c[i]) * 2 < mod return integer_lgs(Y*A, c)
def __init__(self, surface): if isinstance(surface, TranslationSurface): base_ring = surface.base_ring() from flatsurf.geometry.pyflatsurf_conversion import to_pyflatsurf self._surface = to_pyflatsurf(surface) else: from flatsurf.geometry.pyflatsurf_conversion import sage_base_ring base_ring, _ = sage_base_ring(surface) self._surface = surface # A model of the vector space R² in libflatsurf, e.g., to represent the # vector associated to a saddle connection. self.V2 = pyflatsurf.vector.Vectors(base_ring) # We construct a spanning set of edges, that is a subset of the # edges that form a basis of H_1(S, Sigma; Z) # It comes together with a projection matrix t, m = self._spanning_tree() assert set(t.keys()) == set(f[0] for f in self._faces()) self.spanning_set = [] v = set(t.values()) for e in self._surface.edges(): if e.positive() not in v and e.negative() not in v: self.spanning_set.append(e) self.d = len(self.spanning_set) assert 3 * self.d - 3 == self._surface.size() assert m.rank() == self.d m = m.transpose() # projection matrix from Z^E to H_1(S, Sigma; Z) in the basis # of spanning edges self.proj = matrix(ZZ, [r for r in m.rows() if not r.is_zero()]) self.Omega = self._intersection_matrix(t, self.spanning_set) self.V = FreeModule(self.V2.base_ring(), self.d) self.H = matrix(self.V2.base_ring(), self.d, 2) for i in range(self.d): s = self._surface.fromHalfEdge(self.spanning_set[i].positive()) self.H[i] = self.V2._isomorphic_vector_space(self.V2(s)) self.Hdual = self.Omega * self.H # Note that we don't use Sage vector spaces because they are usually # way too slow (in particular we avoid calling .echelonize()) self._U = matrix(self.V2._algebraic_ring(), self.d) self._U_rank = 0 self.update_tangent_space_from_vector(self.H.transpose()[0]) self.update_tangent_space_from_vector(self.H.transpose()[1])
def gen_massmatrix_RNE( do_varify, rbt, usemotordyn = True, varify_trig = True ): from copy import deepcopy Si = _gen_rbt_Si(rbt) IIi = range(0,rbt.dof+1) ### Linear dynamic parameters: for i in range(1,rbt.dof+1): IIi[i] = block_matrix( [ rbt.Ifi[i] , skew(rbt.mli[i]) , -skew(rbt.mli[i]) , rbt.mi[i]*identity_matrix(3) ] ,subdivide=False) ### Not linear dynamic parameters: #for i in range(1,dof+1): IIi[i] = block_matrix( [ rbt.Ifi_from_Ici[i] , skew(rbt.mli_e[i]) , -skew(rbt.mli_e[i]) , rbt.mi[i]*identity_matrix(3) ] ,subdivide=False) def custom_simplify( expression ): #return trig_reduce(expression.expand()).simplify_rational() return expression.simplify_rational() varify_func = None if do_varify: auxvars = [] def varify_func(exp,varrepr): if varify_trig : exp = exp.subs( LoP_to_D(rbt.LoP_trig_f2v) ) exp = custom_simplify( exp ) return m_varify(exp, auxvars, poolrepr='auxM', condition_func=is_compound) M = matrix(SR,rbt.dof,rbt.dof) rbttmp = deepcopy(rbt) rbttmp.grav = zero_matrix(3,1) rbttmp.dq = zero_matrix(rbttmp.dof,1) for i in range(M.nrows()): rbttmp.ddq = zero_matrix(rbttmp.dof,1) rbttmp.ddq[i,0] = 1.0 rbttmp.gen_geom() Vi, dVi = _forward_RNE( rbttmp, Si, varify_func ) Mcoli = _backward_RNE( rbttmp, IIi, Si, Vi, dVi, usemotordyn, None, varify_func = varify_func ) # Do like this since M is symmetric: M[:,i] = matrix(SR,i,1,M[i,:i].list()).stack( Mcoli[i:,:] ) if do_varify: if varify_trig : auxvars = rbt.LoP_trig_v2f + auxvars return auxvars , M else: return M
def small_lgs2(A, c, mod, sol=None): ''' Find short x with Ax = c (mod m). This is more generic because it also works for composite m, but it does not work for c = 0. The difference is that we don't have to compute the orthogonal lattice of A. TODO: For c = 0, we can brute force a k with small ||k|| and solve for c = k*mod instead. See [2] (p. 264-268) and https://crypto.stackexchange.com/questions/37836/ ''' m, n = A.dimensions() A = list(A) for i in range(n): A.append([0]*i + [mod] + [0]*(n-i-1)) A = matrix(ZZ, A) L = A.LLL(delta=0.999999) for i in range(m): assert L[i] == 0 L = L[m:] if c == 0 or c == None: # Brutal heuristic here, see TODO above. We are only trying one single k # here, namely (0, ..., 0, 1) x = normalize(integer_lgs(L, vector([0]*(n-1) + [mod]))) for t in x: assert 0 <= x < mod return x # compute Y such that Y*A == L Y = [] smith = A.transpose().smith_form() for i in range(L.nrows()): y = integer_lgs(None, L[i], smith) # assert At*y == L[i] Y.append(y) Y = matrix(ZZ, Y) # assert Y*A == L c = Y*vector(list(c)+[0]*n)%mod for i in range(len(c)): if c[i] * 2 >= mod: c[i] -= mod assert abs(c[i]) * 2 < mod return integer_lgs(Y*A, c)
def bilinear_matrix(self, sage=False): from .algorithms.wright import bilinearMatrix v, c, d = bilinearMatrix(self) if not sage: return v, c, d # SAGE requires... more stuff from sage.all import SR, matrix l = v.shape[0] v_ = [] c_ = [] for i in range(l * l): v_.append(v[i]._sage_()) c_.append(c[i]._sage_()) return matrix(SR, l, l, v_), matrix(SR, l, l, c_), (dconv(d[0]), dconv(d[1]))
def StrictlyPositiveOrthant(d): if d == 0: return Polyhedron(ambient_dim=0, vertices=[()], base_ring=QQ) else: return Polyhedron(ieqs=block_matrix([[ matrix(QQ, d, 1, [-1 for i in range(d)]), identity_matrix(QQ, d) ]]))
def permute(self, g): """ Returns a new KalmansonSystem obtained by permuting the leaves of self according to permutation p. """ ind = permutation_vector(g) ieqs = matrix(np.vstack([np.array(row)[ind] for row in self._ieqs.rows()])) return KalmansonSystem(self._n, ieqs)
def rrkc_precomputations(self): self.Li = [m.inverse() for m in self.Lt] self.LiK = [self.Kt[i + 1] * self.Li[i] for i in range(self.r)] self.LiC = [self.C[i] * self.Li[i] for i in range(self.r)] mod_Li = [copy(self.Li[i]) for i in range(self.r)] for j in range(self.r): mod_Li[j][self.n - 3 * self.s:, :self.n] = matrix( F, 3 * self.s, self.n) self.precomputed_key_matrix = None self.precomputed_key_matrix_nl = matrix(F, self.n, (self.s * 3) * self.r) self.precomputed_constant = None self.precomputed_constant_nl = vector(F, (self.s * 3) * self.r) for round in range(self.r): tmp = copy(self.LiK[round]) tmpC = copy(self.LiC[round]) for i in range(round + 1, self.r): x = self.LiK[i] c = self.LiC[i] for j in range(i - 1, round - 1, -1): x = x * mod_Li[j] c = c * mod_Li[j] tmp += x tmpC += c # non-linear part idx = round * (3 * self.s) self.precomputed_key_matrix_nl[:self.n, idx:idx + 3 * self.s] = tmp[:self.n, self.n - 3 * self.s:] self.precomputed_constant_nl[idx:idx + 3 * self.s] = tmpC[self.n - 3 * self.s:] # linear part if round == 0: tmp[:, self.n - 3 * self.s:] = matrix(F, self.n, 3 * self.s) tmpC[self.n - 3 * self.s:] = vector(F, 3 * self.s) self.precomputed_key_matrix = tmp self.precomputed_constant = tmpC self.rrkc_precomputations_done = True
def _D_grav_2_zero(self): variables = matrix(SR,self.grav).variables() Dg2z = {} for i in variables: Dg2z[i] = 0 if self.grav.subs(Dg2z).is_zero(): return Dg2z else: return None
def gen_tau_EL(robot, lagrangian=None, usemotordyn=True): L = lagrangian if lagrangian else gen_lagrangian(robot, usemotordyn) tau = matrix(SR, robot.dof, 1) for i in range(0, robot.dof): tau[i, 0] = (L.derivative(robot.dq[i, 0]).subs(robot.D_q_v2f).derivative( robot.t).subs(robot.D_q_f2v) - L.derivative(robot.q[i, 0])) return tau
def padically_evaluate_regular(self, datum): T = datum.toric_datum if not T.is_regular(): raise ValueError('Can only processed regular toric data') M = Set(range(T.length())) q = SR.var('q') alpha = {} for I in Subsets(M): F = [T.initials[i] for i in I] V = SubvarietyOfTorus(F, torus_dim=T.ambient_dim) alpha[I] = V.count() def cat(u, v): return vector(list(u) + list(v)) for I in Subsets(M): cnt = sum((-1)**len(J) * alpha[I + J] for J in Subsets(M - I)) if not cnt: continue P = DirectProductOfPolyhedra(T.polyhedron, StrictlyPositiveOrthant(len(I))) it = iter(identity_matrix(ZZ, len(I)).rows()) ieqs = [] for i in M: ieqs.append( cat( vector(ZZ, (0, )), cat( vector(ZZ, T.initials[i].exponents()[0]) - vector(ZZ, T.lhs[i].exponents()[0]), next(it) if i in I else zero_vector(ZZ, len(I))))) if not ieqs: ieqs = [vector(ZZ, (T.ambient_dim + len(I) + 1) * [0])] Q = Polyhedron(ieqs=ieqs, base_ring=QQ, ambient_dim=T.ambient_dim + len(I)) foo, ring = symbolic_to_ratfun( cnt * (q - 1)**len(I) / q**(T.ambient_dim), [var('t'), var('q')]) corr_cnt = CyclotomicRationalFunction.from_laurent_polynomial( foo, ring) Phi = matrix([ cat(T.integrand[0], zero_vector(ZZ, len(I))), cat(T.integrand[1], vector(ZZ, len(I) * [-1])) ]).transpose() sm = RationalSet([P.intersection(Q)]).generating_function() for z in sm.monomial_substitution(QQ['t', 'q'], Phi): yield corr_cnt * z
def base_change_matrix(self, integral_basis="standard", precision=20): r""" Return the base change matrix to an integral basis. INPUT: - ``integral_basis`` -- a string (default: "standard") - ``precision`` -- a positive integer OUTPUT: An invertible `(n,n)`-matrix `S` over `\mathbb{Q}` with the following property: for an element `a` of `K_0`, the vector ``S*a.vector()`` gives the representation of `a` as a linear combination of ``integral_basis``. TODO: * clarify the role of ``precision`` in this function """ if integral_basis == "standard": return self._S n = self.degree() e = self.ramification_degree() m = self.inertia_degree() S = matrix(QQ, n) pi = self.uniformizer() alpha = self.integral_basis_of_unramified_subfield(precision) for i in range(e): for j in range(m): k = i + e * j alpha_k = (pi**i * alpha[j]).vector() for l in range(n): S[k, l] = alpha_k[l] T = S**(-1) T_reduced = matrix(QQ, n) for i in range(n): for j in range(n): T_reduced[i, j] = self.reduce_rational_number(T[i, j], precision) assert T_reduced.is_invertible( ), "T_reduced= %s\nmust be invertible; T = %s\nprecision = %s" % ( T_reduced, T, precision) return T_reduced
def mpmath_matrix_to_sage(A): entries = list(A) if all(isinstance(e, mp.mpf) for e in entries): F = RealField(mp.prec) entries = [F(e) for e in entries] else: F = ComplexField(mp.prec) entries = [F(e.real, e.imag) for e in entries] return matrix(F, A.rows, A.cols, entries)
def ricci_matrix(walk, g): N = g.num_verts() m = matrix(QQ, N) for i in range(N): for j in range(i+1, N): r = ricci(walk, g, source=i, target=j).ric m[i, j] = r m[j, i] = r return m
def projected_distances(M, v): """ Return the distance vector after modding out by the lineality space of Kalmanson polyhedron given by the Kalmanson inequality matrix M. """ L = M.right_kernel().basis_matrix() B = M.stack(L).transpose() P = matrix(np.diag([1]*M.nrows() + [0]*L.nrows())) return B*P*B**(-1)*v
def padic_right_kernel_matrix(M, flatten_precision=False): # TODO compare against the kernel obtained via the howell form with the same number of digits # if p^prec < 2**63 - 1 d, _, v = padic_smith_form(M) basis = [] val = -Infinity for i in range(M.ncols()): if i >= M.nrows() or d[i, i] == 0: basis.append(v.column(i)) else: val = max(d[i, i].valuation(), val) if flatten_precision: assert M.base_ring()._prec_type() in ['capped-rel', 'capped-abs'] return matrix( Zp(M.base_ring().prime(), prec=M.base_ring().precision_cap() - val, type=M.base_ring()._prec_type()), basis) return matrix(basis)
def test_basis_of_scalar_valued(self): for k in [12, 16, 35, 37, 47]: M = SpaceOfModForms(k, prec=k // 10) dm = M.dimension() self.assertEqual(dm, len(M.basis())) tpls = M.linearly_indep_tuples() self.assertTrue(all(f.wt == k for f in M.basis())) self.assertTrue( matrix([[f[t] for f in M.basis()] for t in tpls]).change_ring(QQ).is_invertible())
def SL2_to_SLN(A, N): F = A.base_ring() R = PolynomialRing(F, ['x', 'y']) x, y = R.gens() X, Y = A * vector(R, (x, y)) monomials = [x**(N - 1 - i) * y**i for i in range(N)] image_vectors = [m(X, Y) for m in monomials] return matrix(F, [[v.monomial_coefficient(m) for m in monomials] for v in image_vectors])
def _to_vector(self, fm, tpls=None): ''' Returns a vector corresponding to fm. By this method, self.basis() becomes the standard basis. ''' if tpls is None: tpls = self.linearly_indep_tuples() m1 = matrix([[f[t] for t in tpls] for f in self.basis()]) v = vector([fm[t] for t in tpls]) return v * m1 ** (-1)
def vect_to_sym(v): n = ZZ(round((-1+sqrt(1+8*len(v)))/2)) M = matrix(n) k = 0 for i in range(n): for j in range(i, n): M[i,j] = v[k] M[j,i] = v[k] k=k+1 return [[int(M[i,j]) for i in range(n)] for j in range(n)]
def ricci_gen(g, problem): # relevant_verts will have unique items starting with source, then target, # then the neighbors of source and target. # Use an OrderedDict to get uniques but preserve order. relevant_verts = OrderedDict.fromkeys( [problem.src, problem.tgt] + list(g.neighbor_iterator(problem.src)) + list(g.neighbor_iterator(problem.tgt))).keys() N = len(relevant_verts) D = matrix(ZZ, N, N) for i in range(N): for j in range(i, N): dist = g.distance(relevant_verts[i], relevant_verts[j]) D[i, j] = dist D[j, i] = dist # We multiply through by problem.denom, so this checks for unit mass: assert(problem.denom == sum(problem.m_src[z] for z in relevant_verts)) assert(problem.denom == sum(problem.m_tgt[z] for z in relevant_verts)) # Set up linear program. p = MixedIntegerLinearProgram() # Note that here and in what follows, i and j are used as the vertices that # correspond to relevant_verts[i] and relevant_verts[j]. # a[i,j] is the (unknown) amount of mass that goes from i to j. # It is constrained to be nonnegative, which are the only inequalities for # our LP. a = p.new_variable(nonnegative=True) # Maximize the negative of the mass transport. p.set_objective( -p.sum(D[i, j]*a[i, j] for i in range(N) for j in range(N))) # The equality constraints simply state that the mass starts in the places # it has diffused to from source... for i in range(N): p.add_constraint( p.sum(a[i, j] for j in range(N)) == problem.m_src[relevant_verts[i]]) # and finishes in the places it has diffused to from target. for j in range(N): p.add_constraint( p.sum(a[i, j] for i in range(N)) == problem.m_tgt[relevant_verts[j]]) W1 = -QQ(p.solve())/problem.denom dist_src_tgt = D[0, 1] # By def'n of relevant_verts. kappa = 1 - W1/dist_src_tgt return Result( coupling={ (relevant_verts[i], relevant_verts[j]): QQ(v/problem.denom) for ((i, j), v) in p.get_values(a).items() if v > 0}, dist=dist_src_tgt, W1=W1, kappa=kappa, ric=kappa)