def _complex_relative(self,n,*args): if len(args)==2: complex = [] for i in range(args[0],args[1]): complex.append(self._complex_relative(n,i)) return complex if n<0 or n>self.poly_ring.ngens(): return [] if n==0: hom_basis = self._p_graded_module(n).homogeneous_part_basis(self.degree+args[0]) return [LogarithmicDifferentialForm(n,b,self) for b in hom_basis] base = self._p_graded_module(n).homogeneous_part_basis(self.degree+args[0]) if len(base)==0: return [] vs_base = VectorSpace(QQ,len(base)) df_base = [LogarithmicDifferentialForm(n,b,self) for b in base] pre_base = self._p_graded_module(n-1).homogeneous_part_basis(self.degree+args[0]) if len(pre_base)==0: return df_base dh = [self.divisor.derivative(g) for g in self.poly_ring.gens()] dh = LogarithmicDifferentialForm(1,dh,self) rel_gens = [] for b in pre_base: b_form = LogarithmicDifferentialForm(n-1,b,self) w = dh.wedge(b_form) rel_gens.append(lift_to_basis([w],df_base)) rel = vs_base.subspace(rel_gens) comp = orth_complement(vs_base,rel) #Lift rel_complex = [] for vec in comp.basis(): rel_complex.append(_weighted_sum(vec,df_base,self)) return rel_complex
def __init__(self, surface, label, point, ring=None, limit=None): self._s = surface if ring is None: self._ring = surface.base_ring() else: self._ring = ring p = surface.polygon(label) point = VectorSpace(self._ring, 2)(point) point.set_immutable() pos = p.get_point_position(point) assert pos.is_inside(), \ "Point must be positioned within the polygon with the given label." # This is the correct thing if point lies in the interior of the polygon with the given label. self._coordinate_dict = {label: {point}} if pos.is_in_edge_interior(): label2, e2 = surface.opposite_edge(label, pos.get_edge()) point2 = surface.edge_transformation(label, pos.get_edge())(point) point2.set_immutable() if label2 in self._coordinate_dict: self._coordinate_dict[label2].add(point2) else: self._coordinate_dict[label2] = {point2} if pos.is_vertex(): self._coordinate_dict = {} sing = surface.singularity(label, pos.get_vertex(), limit=limit) for l, v in sing.vertex_set(): new_point = surface.polygon(l).vertex(v) new_point.set_immutable() if l in self._coordinate_dict: self._coordinate_dict[l].add(new_point) else: self._coordinate_dict[l] = {new_point} # Freeze the sets. for label, point_set in iteritems(self._coordinate_dict): self._coordinate_dict[label] = frozenset(point_set)
def homology(self,varient="complement",*args): hom = {} cc = self.chain_complex(varient,(-1,self.poly_ring.ngens()+1),*args) #Some need wider return range for i in range(self.poly_ring.ngens()+1): vs_am = VectorSpace(QQ,len(cc[i])) if len(cc[i])==0: hom[i] = [] continue if len(cc[i-1])!=0: d_im = [] for b in cc[i-1]: d_b = self.differential(b,varient,*args) d_im.append(lift_to_basis(d_b,cc[i])) img = vs_am.subspace(d_im) else: img = vs_am.subspace([vs_am.zero()]) if len(cc[i+1])!=0: d_ker = [] for b in cc[i]: d_b = self.differential(b,varient,*args) d_ker.append(lift_to_basis(d_b,cc[i+1])) ker = (matrix(QQ,d_ker)).left_kernel() else: ker = vs_am quo = ker.quotient(img) hom[i] = [] for b in quo.basis(): vec = quo.lift(b) part_sum = LogarithmicDifferentialForm.make_zero(i,self) for c,f in zip(vec,cc[i]): part_sum = part_sum + c*f hom[i].append(part_sum) return hom
def ProjectiveGeometryDesign(n, d, F, algorithm=None): """ Returns a projective geometry design. A projective geometry design of parameters `n,d,F` has for points the lines of `F^{n+1}`, and for blocks the `d+1`-dimensional subspaces of `F^{n+1}`, each of which contains `\\frac {|F|^{d+1}-1} {|F|-1}` lines. INPUT: - ``n`` is the projective dimension - ``d`` is the dimension of the subspaces of `P = PPn(F)` which make up the blocks. - ``F`` is a finite field. - ``algorithm`` -- set to ``None`` by default, which results in using Sage's own implementation. In order to use GAP's implementation instead (i.e. its ``PGPointFlatBlockDesign`` function) set ``algorithm="gap"``. Note that GAP's "design" package must be available in this case, and that it can be installed with the ``gap_packages`` spkg. EXAMPLES: The points of the following design are the `\\frac {2^{2+1}-1} {2-1}=7` lines of `\mathbb{Z}_2^{2+1}`. It has `7` blocks, corresponding to each 2-dimensional subspace of `\mathbb{Z}_2^{2+1}`:: sage: designs.ProjectiveGeometryDesign(2, 1, GF(2)) Incidence structure with 7 points and 7 blocks sage: BD = designs.ProjectiveGeometryDesign(2, 1, GF(2), algorithm="gap") # optional - gap_packages (design package) sage: BD.is_block_design() # optional - gap_packages (design package) (True, [2, 7, 3, 1]) """ q = F.order() from sage.interfaces.gap import gap, GapElement from sage.sets.set import Set if algorithm is None: V = VectorSpace(F, n + 1) points = list(V.subspaces(1)) flats = list(V.subspaces(d + 1)) blcks = [] for p in points: b = [] for i in range(len(flats)): if p.is_subspace(flats[i]): b.append(i) blcks.append(b) v = (q**(n + 1) - 1) / (q - 1) return BlockDesign(v, blcks, name="ProjectiveGeometryDesign") if algorithm == "gap": # Requires GAP's Design gap.load_package("design") gap.eval("D := PGPointFlatBlockDesign( %s, %s, %d )" % (n, q, d)) v = eval(gap.eval("D.v")) gblcks = eval(gap.eval("D.blocks")) gB = [] for b in gblcks: gB.append([x - 1 for x in b]) return BlockDesign(v, gB, name="ProjectiveGeometryDesign")
def __init__(self, surface, label, point, ring = None, limit=None): self._s = surface if ring is None: self._ring = surface.base_ring() else: self._ring = ring p = surface.polygon(label) point = VectorSpace(self._ring,2)(point) point.set_immutable() pos = p.get_point_position(point) assert pos.is_inside(), \ "Point must be positioned within the polygon with the given label." # This is the correct thing if point lies in the interior of the polygon with the given label. self._coordinate_dict = {label: {point}} if pos.is_in_edge_interior(): label2,e2 = surface.opposite_edge(label, pos.get_edge()) point2 = surface.edge_transformation(label, pos.get_edge())(point) point2.set_immutable() if label2 in self._coordinate_dict: self._coordinate_dict[label2].add(point2) else: self._coordinate_dict[label2]={point2} if pos.is_vertex(): self._coordinate_dict = {} sing = surface.singularity(label, pos.get_vertex(), limit=limit) for l,v in sing.vertex_set(): new_point = surface.polygon(l).vertex(v) new_point.set_immutable() if l in self._coordinate_dict: self._coordinate_dict[l].add(new_point) else: self._coordinate_dict[l] = {new_point} # Freeze the sets. for label,point_set in self._coordinate_dict.iteritems(): self._coordinate_dict[label] = frozenset(point_set)
def _puncture(v, points): r""" Returns v punctured as the positions listed in ``points``. INPUT: - ``v`` -- a vector or a list of vectors - ``points`` -- a set of integers, or an integer EXAMPLES:: sage: v = vector(GF(7), (2,3,0,2,1,5,1,5,6,5,3)) sage: from sage.coding.punctured_code import _puncture sage: _puncture(v, {4, 3}) (2, 3, 0, 5, 1, 5, 6, 5, 3) """ if not isinstance(points, (Integer, int, set)): raise TypeError( "points must be either a Sage Integer, a Python int, or a set") if isinstance(v, list): size = len(v[0]) S = VectorSpace(v[0].base_ring(), size - len(points)) l = [] for i in v: new_v = [i[j] for j in range(size) if j not in points] l.append(S(new_v)) return l S = VectorSpace(v.base_ring(), len(v) - len(points)) new_v = [v[i] for i in range(len(v)) if i not in points] return S(new_v)
def nonisomorphic_cubes_Z2(n, avoid_complete=False): """ Returns a generator for all n-dimensional Cube-like graphs (Cayley graphs over Z_2^n) with their generators. With avoid_complete=True avoids the complete graph. Iterates over tuples (generatorSet, G). """ vs = VectorSpace(GF(2), n) basegens = vs.gens() optgens = [v for v in vs if sum(map(int,v)) >= 2] total = 2**len(optgens) seen_graphs = set() c = 0 for g in powerset(optgens): c += 1 gens = tuple(list(basegens) + g) if c % (total / 100 + 1) == 0: log.debug("Generating (%d of %d)" % (c, total)) if avoid_complete: if len(g) >= len(optgens): continue G = CayleyGraph(vs, gens) canon = tuple(Graph(G).canonical_label().edges()) if canon in seen_graphs: continue log.debug("Unique graph (%d of %d) gens=%s" % (c, total, gens)) seen_graphs.add(canon) yield (gens, G)
def test_satisfy_inter(self): v_space = VectorSpace(QQ,4) sub = v_space.subspace([[1,-1,1,1],[2,-3,4,5]]) comp = orth_complement(v_space,sub) zero = v_space.subspace([v_space.zero()]) inter = sub.intersection(comp) self.assertEqual(zero,inter)
def ProjectiveGeometryDesign(n, d, F, algorithm=None): """ Returns a projective geometry design. A projective geometry design of parameters `n,d,F` has for points the lines of `F^{n+1}`, and for blocks the `d+1`-dimensional subspaces of `F^{n+1}`, each of which contains `\\frac {|F|^{d+1}-1} {|F|-1}` lines. INPUT: - ``n`` is the projective dimension - ``d`` is the dimension of the subspaces of `P = PPn(F)` which make up the blocks. - ``F`` is a finite field. - ``algorithm`` -- set to ``None`` by default, which results in using Sage's own implementation. In order to use GAP's implementation instead (i.e. its ``PGPointFlatBlockDesign`` function) set ``algorithm="gap"``. Note that GAP's "design" package must be available in this case, and that it can be installed with the ``gap_packages`` spkg. EXAMPLES: The points of the following design are the `\\frac {2^{2+1}-1} {2-1}=7` lines of `\mathbb{Z}_2^{2+1}`. It has `7` blocks, corresponding to each 2-dimensional subspace of `\mathbb{Z}_2^{2+1}`:: sage: designs.ProjectiveGeometryDesign(2, 1, GF(2)) Incidence structure with 7 points and 7 blocks sage: BD = designs.ProjectiveGeometryDesign(2, 1, GF(2), algorithm="gap") # optional - gap_packages (design package) sage: BD.is_block_design() # optional - gap_packages (design package) (True, [2, 7, 3, 1]) """ q = F.order() from sage.interfaces.gap import gap, GapElement from sage.sets.set import Set if algorithm is None: V = VectorSpace(F, n+1) points = list(V.subspaces(1)) flats = list(V.subspaces(d+1)) blcks = [] for p in points: b = [] for i in range(len(flats)): if p.is_subspace(flats[i]): b.append(i) blcks.append(b) v = (q**(n+1)-1)/(q-1) return BlockDesign(v, blcks, name="ProjectiveGeometryDesign") if algorithm == "gap": # Requires GAP's Design gap.load_package("design") gap.eval("D := PGPointFlatBlockDesign( %s, %s, %d )"%(n,q,d)) v = eval(gap.eval("D.v")) gblcks = eval(gap.eval("D.blocks")) gB = [] for b in gblcks: gB.append([x-1 for x in b]) return BlockDesign(v, gB, name="ProjectiveGeometryDesign")
def nonisomorphic_cubes_Z2(n, avoid_complete=False): """ Returns a generator for all n-dimensional Cube-like graphs (Cayley graphs over Z_2^n) with their generators. With avoid_complete=True avoids the complete graph. Iterates over tuples (generatorSet, G). """ vs = VectorSpace(GF(2), n) basegens = vs.gens() optgens = [v for v in vs if sum(map(int, v)) >= 2] total = 2**len(optgens) seen_graphs = set() c = 0 for g in powerset(optgens): c += 1 gens = tuple(list(basegens) + g) if c % (total / 100 + 1) == 0: log.debug("Generating (%d of %d)" % (c, total)) if avoid_complete: if len(g) >= len(optgens): continue G = CayleyGraph(vs, gens) canon = tuple(Graph(G).canonical_label().edges()) if canon in seen_graphs: continue log.debug("Unique graph (%d of %d) gens=%s" % (c, total, gens)) seen_graphs.add(canon) yield (gens, G)
def ProjectiveGeometryDesign(n, d, F, algorithm=None): """ INPUT: - ``n`` is the projective dimension - ``v`` is the number of points `PPn(GF(q))` - ``d`` is the dimension of the subspaces of `P = PPn(GF(q))` which make up the blocks - ``b`` is the number of `d`-dimensional subspaces of `P` Wraps GAP Design's PGPointFlatBlockDesign. Does *not* require GAP's Design. EXAMPLES:: sage: designs.ProjectiveGeometryDesign(2, 1, GF(2)) Incidence structure with 7 points and 7 blocks sage: BD = designs.ProjectiveGeometryDesign(2, 1, GF(2), algorithm="gap") # optional - gap_packages (design package) sage: BD.is_block_design() # optional - gap_packages (design package) (True, [2, 7, 3, 1]) """ q = F.order() from sage.interfaces.gap import gap, GapElement from sage.sets.set import Set if algorithm == None: V = VectorSpace(F, n+1) points = list(V.subspaces(1)) flats = list(V.subspaces(d+1)) blcks = [] for p in points: b = [] for i in range(len(flats)): if p.is_subspace(flats[i]): b.append(i) blcks.append(b) v = (q**(n+1)-1)/(q-1) return BlockDesign(v, blcks, name="ProjectiveGeometryDesign") if algorithm == "gap": # Requires GAP's Design gap.load_package("design") gap.eval("D := PGPointFlatBlockDesign( %s, %s, %d )"%(n,q,d)) v = eval(gap.eval("D.v")) gblcks = eval(gap.eval("D.blocks")) gB = [] for b in gblcks: gB.append([x-1 for x in b]) return BlockDesign(v, gB, name="ProjectiveGeometryDesign")
def ProjectiveGeometryDesign(n, d, F, algorithm=None): """ INPUT: - ``n`` is the projective dimension - ``v`` is the number of points `PPn(GF(q))` - ``d`` is the dimension of the subspaces of `P = PPn(GF(q))` which make up the blocks - ``b`` is the number of `d`-dimensional subspaces of `P` Wraps GAP Design's PGPointFlatBlockDesign. Does *not* require GAP's Design. EXAMPLES:: sage: designs.ProjectiveGeometryDesign(2, 1, GF(2)) Incidence structure with 7 points and 7 blocks sage: BD = designs.ProjectiveGeometryDesign(2, 1, GF(2), algorithm="gap") # optional - gap_packages (design package) sage: BD.is_block_design() # optional - gap_packages (design package) (True, [2, 7, 3, 1]) """ q = F.order() from sage.interfaces.gap import gap, GapElement from sage.sets.set import Set if algorithm == None: V = VectorSpace(F, n + 1) points = list(V.subspaces(1)) flats = list(V.subspaces(d + 1)) blcks = [] for p in points: b = [] for i in range(len(flats)): if p.is_subspace(flats[i]): b.append(i) blcks.append(b) v = (q**(n + 1) - 1) / (q - 1) return BlockDesign(v, blcks, name="ProjectiveGeometryDesign") if algorithm == "gap": # Requires GAP's Design gap.load_package("design") gap.eval("D := PGPointFlatBlockDesign( %s, %s, %d )" % (n, q, d)) v = eval(gap.eval("D.v")) gblcks = eval(gap.eval("D.blocks")) gB = [] for b in gblcks: gB.append([x - 1 for x in b]) return BlockDesign(v, gB, name="ProjectiveGeometryDesign")
def ambient_vector_space(self): """ Return the ambient vector space. .. SEEALSO:: :meth:`ambient_module` OUTPUT: The vector space (over the fraction field of the base ring) where the linear expressions live. EXAMPLES:: sage: from sage.geometry.linear_expression import LinearExpressionModule sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z')) sage: L.ambient_vector_space() Vector space of dimension 3 over Rational Field sage: M = LinearExpressionModule(ZZ, ('r', 's')) sage: M.ambient_module() Ambient free module of rank 2 over the principal ideal domain Integer Ring sage: M.ambient_vector_space() Vector space of dimension 2 over Rational Field """ from sage.modules.free_module import VectorSpace field = self.base_ring().fraction_field() return VectorSpace(field, self.ngens())
def Vrepresentation_space(self): r""" Return the ambient vector space. This is the vector space or module containing the Vrepresentation vectors. OUTPUT: A free module over the base ring of dimension :meth:`ambient_dim`. EXAMPLES:: sage: from sage.geometry.polyhedron.parent import Polyhedra sage: Polyhedra(QQ, 4).Vrepresentation_space() Vector space of dimension 4 over Rational Field sage: Polyhedra(QQ, 4).ambient_space() Vector space of dimension 4 over Rational Field """ if self.base_ring() in Fields(): from sage.modules.free_module import VectorSpace return VectorSpace(self.base_ring(), self.ambient_dim()) else: from sage.modules.free_module import FreeModule return FreeModule(self.base_ring(), self.ambient_dim())
def __init__(self, space, number_errors, number_erasures): r""" TESTS: If the sum of number of errors and number of erasures exceeds (or may exceed, in the case of tuples) the dimension of the input space, it will return an error:: sage: n_err, n_era = 21, 21 sage: Chan = channels.ErrorErasureChannel(GF(59)^40, n_err, n_era) Traceback (most recent call last): ... ValueError: The total number of errors and erasures can not exceed the dimension of the input space """ if isinstance(number_errors, (Integer, int)): number_errors = (number_errors, number_errors) if not isinstance(number_errors, (tuple, list)): raise ValueError("number_errors must be a tuple, a list, an Integer or a Python int") if isinstance(number_erasures, (Integer, int)): number_erasures = (number_erasures, number_erasures) if not isinstance(number_erasures, (tuple, list)): raise ValueError("number_erasures must be a tuple, a list, an Integer or a Python int") output_space = cartesian_product([space, VectorSpace(GF(2), space.dimension())]) super(ErrorErasureChannel, self).__init__(space, output_space) if number_errors[1] + number_erasures[1] > space.dimension(): raise ValueError("The total number of errors and erasures can not exceed the dimension of the input space") self._number_errors = number_errors self._number_erasures = number_erasures
def _differential_relative(self,form,*args): n = form.degree deg = self._p_graded_module(n).total_degree(form.vec) deg -= self.degree der = form.derivative() full = self._p_graded_module(n+1).homogeneous_part_basis(self.degree+deg) full_forms = [LogarithmicDifferentialForm(n+1,b,self) for b in full] full_space = VectorSpace(QQ,len(full)) target_comp = self._complex_relative(n+1,deg) comp_vecs = [] for b in target_comp: comp_vecs.append(lift_to_basis([b],full_forms)) comp_vecs = full_space.subspace(comp_vecs) lift_full = lift_to_basis([der],full_forms) lift_prog = comp_vecs.basis_matrix()*vector(lift_full) return [_weighted_sum(lift_prog,target_comp,self)]
def _repr_vector_space(self, dim): """ Return a string representation of the vector space of given dimension INPUT: - ``dim`` -- integer. OUTPUT: String representation of the vector space of dimension ``dim``. EXAMPLES:: sage: F = FilteredVectorSpace(3, base_ring=RDF) sage: F._repr_vector_space(1234) 'RDF^1234' sage: F3 = FilteredVectorSpace(3, base_ring=GF(3)) sage: F3._repr_vector_space(1234) 'GF(3)^1234' sage: F3 = FilteredVectorSpace(3, base_ring=AA) sage: F3._repr_vector_space(1234) 'Vector space of dimension 1234 over Algebraic Real Field' """ if dim == 0: return '0' try: return self._repr_field_name() + '^' + str(dim) except NotImplementedError: return repr(VectorSpace(self.base_ring(), dim))
def __init__(self, center, radius_squared, base_ring=None): r""" Construct a circle from a Vector representing the center, and the radius squared. """ if base_ring is None: self._base_ring = radius_squared.parent() else: self._base_ring = base_ring # for calculations: self._V2 = VectorSpace(self._base_ring,2) self._V3 = VectorSpace(self._base_ring,3) self._center = self._V2(center) self._radius_squared = self._base_ring(radius_squared)
def __init__(self, similarity_surface, ring=None): self._s = similarity_surface if ring is None: self._base_ring = self._s.base_ring() else: self._base_ring = ring from sage.modules.free_module import VectorSpace self._V = VectorSpace(self._base_ring, 2)
def test(): "Run unit tests for the module." log.getLogger().setLevel(log.DEBUG) from sage.graphs.graph_generators import graphs K2 = graphs.CompleteGraph(2) K4 = graphs.CompleteGraph(4) Z2_3 = VectorSpace(GF(2), 3) Z2_2 = VectorSpace(GF(2), 2) # nonisomorphic_cubes_Z2 assert len(list(nonisomorphic_cubes_Z2(1))) == 1 assert len(list(nonisomorphic_cubes_Z2(2))) == 2 assert len(list(nonisomorphic_cubes_Z2(3))) == 6 # CayleyGraph K4b = CayleyGraph(Z2_2, [(1, 0), (0, 1), (1, 1)]) assert K4.is_isomorphic(K4b) # squash_cube Ge = CayleyGraph(Z2_2, [(1, 0), (0, 1)]) Smap = squash_cube(Ge, (1, 1)) K2e = Ge.subgraph(Smap.values()) assert K2.is_isomorphic(K2e) assert is_hom(Ge, K2e, Smap) # hom_is_by_subspace Gg = CayleyGraph(Z2_3, [(1, 0, 0), (0, 1, 0)]) Hg = CayleyGraph(Z2_2, [(1, 0), (0, 1), (1, 1)]) homg = { (0, 0, 0): (0, 0), (1, 0, 0): (1, 0), (0, 1, 0): (0, 1), (1, 1, 0): (1, 1), (0, 0, 1): (0, 0), (1, 0, 1): (1, 0), (0, 1, 1): (1, 1), (1, 1, 1): (0, 1) } assert is_hom(Gg, Hg, homg) assert hom_is_by_subspace(Gg, Z2_3, homg, require_isomorphic=False) assert not hom_is_by_subspace(Gg, Z2_3, homg, require_isomorphic=True) for h in extend_hom(Gg, K2, limit=10): assert hom_is_by_subspace(Gg, Z2_3, h, require_isomorphic=True) log.info("All tests passed.")
def squash_cube(G, c): """ Assuming G is a cube-like _undirected_ graph (Cayley over Z_2^n), given a vector c from Z_2^n, finds a homomorphism unifying vectors differing by c. One of the vertives (v, v+c) still must be chosen as the target for the homomorphism in each such pair. Returns a hom map to a subgraph or None if none such exists. """ n = len(c) vs = VectorSpace(GF(2), n) c = vs(c) pairs = {} # vertex -> pair for v in G.vertices(): w = tuple(vs(v) + c) if G.has_edge(v, w): return False # contracting an edge! if w in pairs or v in pairs: continue pairs[v] = (v, w) pairs[w] = (v, w) chosen = {} # pair -> 0 or 1 (first or second in the pair) undecided = set(pairs.values()) # undecided pairs def extend(pair, sel): """Extend `chosen[]` by setting pair to sel (0 or 1). Recursively chooses for pairs forced by this coloring. Returns False on inconsistency, True if all the forcing succeeded.""" if pair in chosen: if chosen[pair] != sel: return False return True chosen[pair] = sel if pair in undecided: undecided.remove(pair) v = pair[sel] w = pair[1 - sel] for n in G.neighbors(v): np = pairs[n] ni = np.index(n) if G.has_edge(n, w): continue if not extend(np, ni): return False return True while undecided: p = undecided.pop() if not extend(p, 0): return None # create the hom-map hom = {} for p, sel in chosen.items(): hom[p[0]] = p[sel] hom[p[1]] = p[sel] return hom
def reduced_charges(M): """ Given a snappy manifold M, we find all reduced charges so that: (1) no loop in the triangulation passes an odd number of pi's. (2) no tetrahedron has three pi's and """ out = sol_and_kernel(M) x, A = out nt = M.num_tetrahedra() dim = 3 * nt V = VectorSpace(ZZ2, dim) AA = V.subspace(A) # the reduced kernel xx = V(x) # the reduced solution charges = [xx + sum(B) for B in powerset(AA.basis())] charges = [c for c in charges if not has_pi_triple(c) ] # reject if there are three pi's in any tet. return charges
def cycle_space(self, degree): r""" Returns the space of cycles of degree i Zi = ker(der: C_i -> C_{i-1}) """ assert (degree > -2 and degree < 3) if degree == -1: return VectorSpace(QQ, 1) return self._objects[degree].cycle_space()
def ambient_space(self): r""" Returns the ambient vector space of ``self``. EXAMPLES:: sage: C = codes.HammingCode(GF(2), 3) sage: C.ambient_space() Vector space of dimension 7 over Finite Field of size 2 """ return VectorSpace(self.base_ring(),self.length())
def circle_from_three_points(p,q,r,base_ring=None): r""" Construct a circle from three points on the circle. """ if base_ring is None: base_ring=p.base_ring() V2 = VectorSpace(base_ring.fraction_field(), 2) V3 = VectorSpace(base_ring.fraction_field(), 3) v1=V3((p[0]+q[0],p[1]+q[1],2)) v2=V3((p[1]-q[1],q[0]-p[0],0)) line1=v1.cross_product(v2) v1=V3((p[0]+r[0],p[1]+r[1],2)) v2=V3((p[1]-r[1],r[0]-p[0],0)) line2=v1.cross_product(v2) center_3 = line1.cross_product(line2) if center_3[2].is_zero(): raise ValueError("The three points lie on a line.") center = V2( (center_3[0]/center_3[2], center_3[1]/center_3[2]) ) return Circle(center, (p[0]-center[0])**2+(p[1]-center[1])**2 )
def message_space(self): r""" Return the message space of ``self``. EXAMPLES:: sage: C = codes.ParityCheckCode(GF(5),7) sage: E = codes.encoders.ParityCheckCodeStraightforwardEncoder(C) sage: E.message_space() Vector space of dimension 7 over Finite Field of size 5 """ return VectorSpace(self.code().base_field(), self.code().dimension())
def __init__(self, base_field, length, default_encoder_name, default_decoder_name, metric='Hamming'): """ Initializes mandatory parameters that any linear code shares. This method only exists for inheritance purposes as it initializes parameters that need to be known by every linear code. The class :class:`sage.coding.linear_code_no_metric.AbstractLinearCodeNoMetric` should never be directly instantiated. INPUT: - ``base_field`` -- the base field of ``self`` - ``length`` -- the length of ``self`` (a Python int or a Sage Integer, must be > 0) - ``default_encoder_name`` -- the name of the default encoder of ``self`` - ``default_decoder_name`` -- the name of the default decoder of ``self`` - ``metric`` -- (default: ``Hamming``) the metric of ``self`` """ self._registered_encoders['Systematic'] = LinearCodeSystematicEncoder if not base_field.is_field(): raise ValueError("'base_field' must be a field (and {} is not one)".format(base_field)) if not default_encoder_name in self._registered_encoders: raise ValueError("You must set a valid encoder as default encoder for this code, by filling in the dictionary of registered encoders") if not default_decoder_name in self._registered_decoders: raise ValueError("You must set a valid decoder as default decoder for this code, by filling in the dictionary of registered decoders") #if not self.dimension() <= length: # raise ValueError("The dimension of the code can be at most its length, {}".format(length)) super(AbstractLinearCodeNoMetric, self).__init__(length, default_encoder_name, default_decoder_name, metric) cat = Modules(base_field).FiniteDimensional().WithBasis().Finite() facade_for = VectorSpace(base_field, self._length) self.Element = type(facade_for.an_element()) #for when we made this a non-facade parent Parent.__init__(self, base=base_field, facade=facade_for, category=cat)
def chain_space(self, degree): r""" Chain space. INPUT: - ``degree`` -- either """ assert (degree > -2 and degree < 3) if degree == -1: return VectorSpace(QQ, 1) return self._objects[degree].chain_space()
def tiny_integrals_on_basis(self, P, Q): r""" Evaluate the integrals `\{\int_P^Q x^i dx/2y \}_{i=0}^{2g-1}` by formally integrating a power series in a local parameter `t`. `P` and `Q` MUST be in the same residue disc for this result to make sense. INPUT: - P a point on self - Q a point on self (in the same residue disc as P) OUTPUT: The integrals `\{\int_P^Q x^i dx/2y \}_{i=0}^{2g-1}` EXAMPLES:: sage: K = pAdicField(17, 5) sage: E = EllipticCurve(K, [-31/3, -2501/108]) # 11a sage: P = E(K(14/3), K(11/2)) sage: TP = E.teichmuller(P); sage: E.tiny_integrals_on_basis(P, TP) (17 + 14*17^2 + 17^3 + 8*17^4 + O(17^5), 16*17 + 5*17^2 + 8*17^3 + 14*17^4 + O(17^5)) :: sage: K = pAdicField(11, 5) sage: x = polygen(K) sage: C = HyperellipticCurve(x^5 + 33/16*x^4 + 3/4*x^3 + 3/8*x^2 - 1/4*x + 1/16) sage: P = C.lift_x(11^(-2)) sage: Q = C.lift_x(3*11^(-2)) sage: C.tiny_integrals_on_basis(P,Q) (3*11^3 + 7*11^4 + 4*11^5 + 7*11^6 + 5*11^7 + O(11^8), 3*11 + 10*11^2 + 8*11^3 + 9*11^4 + 7*11^5 + O(11^6), 4*11^-1 + 2 + 6*11 + 6*11^2 + 7*11^3 + O(11^4), 11^-3 + 6*11^-2 + 2*11^-1 + 2 + O(11^2)) Note that this fails if the points are not in the same residue disc:: sage: S = C(0,1/4) sage: C.tiny_integrals_on_basis(P,S) Traceback (most recent call last): ... ValueError: (11^-2 + O(11^3) : 11^-5 + 8*11^-2 + O(11^0) : 1 + O(11^5)) and (0 : 3 + 8*11 + 2*11^2 + 8*11^3 + 2*11^4 + O(11^5) : 1 + O(11^5)) are not in the same residue disc """ if P == Q: V = VectorSpace(self.base_ring(), 2 * self.genus()) return V(0) R = PolynomialRing(self.base_ring(), ['x', 'y']) x, y = R.gens() return self.tiny_integrals([x**i for i in range(2 * self.genus())], P, Q)
def make_random_instance(n, m): solution = VectorSpace(ZZ2, n).random_element() results = [] quad_forms = [] for _ in range(m): e = [] for _ in range(n * (n + 1) / 2): e.append(ZZ2.random_element()) quad_form = QuadraticForm(ZZ2, n, e) quad_forms.append(quad_form) results.append(quad_form(solution)) instance = Instance(n, quad_forms, results) prover = Prover(instance, solution) return (instance, prover)
def HomologyGroup(n, base_ring, invfac=None): """ Abelian group on `n` generators which represents a homology group in a fixed degree. INPUT: - ``n`` -- integer; the number of generators - ``base_ring`` -- ring; the base ring over which the homology is computed - ``inv_fac`` -- list of integers; the invariant factors -- ignored if the base ring is a field OUTPUT: A class that can represent the homology group in a fixed homological degree. EXAMPLES:: sage: from sage.homology.homology_group import HomologyGroup sage: G = AbelianGroup(5, [5,5,7,8,9]); G Multiplicative Abelian group isomorphic to C5 x C5 x C7 x C8 x C9 sage: H = HomologyGroup(5, ZZ, [5,5,7,8,9]); H C5 x C5 x C7 x C8 x C9 sage: AbelianGroup(4) Multiplicative Abelian group isomorphic to Z x Z x Z x Z sage: HomologyGroup(4, ZZ) Z x Z x Z x Z sage: HomologyGroup(100, ZZ) Z^100 """ if base_ring.is_field(): return VectorSpace(base_ring, n) # copied from AbelianGroup: if invfac is None: if isinstance(n, (list, tuple)): invfac = n n = len(n) else: invfac = [] if len(invfac) < n: invfac = [0] * (n - len(invfac)) + invfac elif len(invfac) > n: raise ValueError("invfac (={}) must have length n (={})".format( invfac, n)) M = HomologyGroup_class(n, invfac) return M
def chain_space(self): r""" Return the space of chain on this finite set of vertices. EXAMPLES:: sage: from surface_dynamics.all import * sage: o = Origami('(1,2,3,4)', '(1,5)') sage: V = o._vertices() sage: V.chain_space() Vector space of dimension 3 over Rational Field """ return VectorSpace(QQ, self.cardinality())
def ambient_vector_space(self): """ Return the ambient (unfiltered) vector space. OUTPUT: A vector space. EXAMPLES:: sage: V = FilteredVectorSpace(1, 0) sage: V.ambient_vector_space() Vector space of dimension 1 over Rational Field """ return VectorSpace(self.base_ring(), self.dimension())
def _smallscale_present_linearlayer(nsboxes=16): """ .. TODO:: switch to sage.crypto.linearlayer (:trac:`25735`) as soon as it is included in sage EXAMPLES:: sage: from sage.crypto.block_cipher.present import _smallscale_present_linearlayer sage: _smallscale_present_linearlayer(4) [1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0] [0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0] [0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0] [0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1] """ from sage.modules.free_module import VectorSpace from sage.modules.free_module_element import vector from sage.matrix.constructor import Matrix from sage.rings.finite_rings.finite_field_constructor import GF def present_llayer(n, x): dim = 4 * n y = [0] * dim for i in range(dim - 1): y[i] = x[(n * i) % (dim - 1)] y[dim - 1] = x[dim - 1] return vector(GF(2), y) m = Matrix(GF(2), [ present_llayer(nsboxes, ei) for ei in VectorSpace(GF(2), 4 * nsboxes).basis() ]) return m
def ambient_vector_space(self): """ Return the ambient (unfiltered) vector space. OUTPUT: A vector space. EXAMPLES:: sage: V = FilteredVectorSpace(2, 0) sage: W = FilteredVectorSpace(2, 2) sage: F = MultiFilteredVectorSpace({'a':V, 'b':W}) sage: F.ambient_vector_space() Vector space of dimension 2 over Rational Field """ return VectorSpace(self.base_ring(), self.dimension())
def Hrepresentation_space(self): r""" Return the linear space containing the H-representation vectors. OUTPUT: A free module over the base ring of dimension :meth:`ambient_dim` + 1. EXAMPLES:: sage: from sage.geometry.polyhedron.parent import Polyhedra sage: Polyhedra(ZZ, 2).Hrepresentation_space() Ambient free module of rank 3 over the principal ideal domain Integer Ring """ if self.base_ring() in Fields(): from sage.modules.free_module import VectorSpace return VectorSpace(self.base_ring(), self.ambient_dim() + 1) else: from sage.modules.free_module import FreeModule return FreeModule(self.base_ring(), self.ambient_dim() + 1)
def construct_from_generators_indices(generators, filtration, base_ring, check): """ Construct a filtered vector space. INPUT: - ``generators`` -- a list/tuple/iterable of vectors, or something convertible to them. The generators spanning various subspaces. - ``filtration`` -- a list or iterable of filtration steps. Each filtration step is a pair ``(degree, ray_indices)``. The ``ray_indices`` are a list or iterable of ray indices, which span a subspace of the vector space. The integer ``degree`` stipulates that all filtration steps of degree higher or equal than ``degree`` (up to the next filtration step) are said subspace. EXAMPLES:: sage: from sage.modules.filtered_vector_space import construct_from_generators_indices sage: gens = [(1,0), (0,1), (-1,-1)] sage: V = construct_from_generators_indices(gens, {1:[0,1], 3:[1]}, QQ, True); V QQ^2 >= QQ^1 >= QQ^1 >= 0 TESTS:: sage: gens = [(int(1),int(0)), (0,1), (-1,-1)] sage: construct_from_generators_indices(iter(gens), {int(0):[0, int(1)], 2:[2]}, QQ, True) QQ^2 >= QQ^1 >= QQ^1 >= 0 """ # normalize generators generators = map(list, generators) # deduce dimension if len(generators) == 0: dim = ZZ(0) else: dim = ZZ(len(generators[0])) ambient = VectorSpace(base_ring, dim) # complete generators to a generating set if matrix(base_ring, generators).rank() < dim: complement = ambient.span(generators).complement() generators = generators + list(complement.gens()) # normalize generators II generators = tuple(ambient(v) for v in generators) for v in generators: v.set_immutable() # normalize filtration data normalized = dict() for deg, gens in iteritems(filtration): deg = normalize_degree(deg) gens = map(ZZ, gens) if any(i < 0 or i >= len(generators) for i in gens): raise ValueError('generator index out of bounds') normalized[deg] = tuple(sorted(gens)) try: del normalized[minus_infinity] except KeyError: pass filtration = normalized return FilteredVectorSpace_class(base_ring, dim, generators, filtration, check=check)
def mcfarland_1973_construction(q, s): r""" Return a difference set. The difference set returned has the following parameters .. MATH:: v = \frac{q^{s+1}(q^{s+1}+q-2)}{q-1}, k = \frac{q^s (q^{s+1}-1)}{q-1}, \lambda = \frac{q^s(q^s-1)}{q-1} This construction is due to [McF1973]_. INPUT: - ``q``, ``s`` - (integers) parameters for the difference set (see the above formulas for the expression of ``v``, ``k``, ``l`` in terms of ``q`` and ``s``) .. SEEALSO:: The function :func:`are_mcfarland_1973_parameters` makes the translation between the parameters `(q,s)` corresponding to a given triple `(v,k,\lambda)`. REFERENCES: .. [McF1973] Robert L. McFarland "A family of difference sets in non-cyclic groups" Journal of Combinatorial Theory (A) vol 15 (1973). http://dx.doi.org/10.1016/0097-3165(73)90031-9 EXAMPLES:: sage: from sage.combinat.designs.difference_family import ( ....: mcfarland_1973_construction, is_difference_family) sage: G,D = mcfarland_1973_construction(3, 1) sage: assert is_difference_family(G, D, 45, 12, 3) sage: G,D = mcfarland_1973_construction(2, 2) sage: assert is_difference_family(G, D, 64, 28, 12) """ from sage.rings.finite_rings.finite_field_constructor import GF from sage.modules.free_module import VectorSpace from sage.rings.finite_rings.integer_mod_ring import Zmod from sage.categories.cartesian_product import cartesian_product from itertools import izip r = (q**(s+1)-1) // (q-1) F = GF(q,'a') V = VectorSpace(F, s+1) K = Zmod(r+1) G = cartesian_product([F]*(s+1) + [K]) D = [] for k,H in izip(K, V.subspaces(s)): for v in H: D.append(G((tuple(v) + (k,)))) return G,[D]
def construct_from_generators_indices(generators, filtration, base_ring, check): """ Construct a filtered vector space. INPUT: - ``generators`` -- a list/tuple/iterable of vectors, or something convertible to them. The generators spanning various subspaces. - ``filtration`` -- a list or iterable of filtration steps. Each filtration step is a pair ``(degree, ray_indices)``. The ``ray_indices`` are a list or iterable of ray indices, which span a subspace of the vector space. The integer ``degree`` stipulates that all filtration steps of degree higher or equal than ``degree`` (up to the next filtration step) are said subspace. EXAMPLES:: sage: from sage.modules.filtered_vector_space import construct_from_generators_indices sage: gens = [(1,0), (0,1), (-1,-1)] sage: V = construct_from_generators_indices(gens, {1:[0,1], 3:[1]}, QQ, True); V QQ^2 >= QQ^1 >= QQ^1 >= 0 TESTS:: sage: gens = [(int(1),int(0)), (0,1), (-1,-1)] sage: construct_from_generators_indices(iter(gens), {int(0):[0, int(1)], 2:[2]}, QQ, True) QQ^2 >= QQ^1 >= QQ^1 >= 0 """ # normalize generators generators = [list(g) for g in generators] # deduce dimension if len(generators) == 0: dim = ZZ(0) else: dim = ZZ(len(generators[0])) ambient = VectorSpace(base_ring, dim) # complete generators to a generating set if matrix(base_ring, generators).rank() < dim: complement = ambient.span(generators).complement() generators = generators + list(complement.gens()) # normalize generators II generators = tuple(ambient(v) for v in generators) for v in generators: v.set_immutable() # normalize filtration data normalized = dict() for deg, gens in filtration.items(): deg = normalize_degree(deg) gens = [ZZ(i) for i in gens] if any(i < 0 or i >= len(generators) for i in gens): raise ValueError('generator index out of bounds') normalized[deg] = tuple(sorted(gens)) try: del normalized[minus_infinity] except KeyError: pass filtration = normalized return FilteredVectorSpace_class(base_ring, dim, generators, filtration, check=check)
def algebraic_topological_model(K, base_ring=None): r""" Algebraic topological model for cell complex ``K`` with coefficients in the field ``base_ring``. INPUT: - ``K`` -- either a simplicial complex or a cubical complex - ``base_ring`` -- coefficient ring; must be a field OUTPUT: a pair ``(phi, M)`` consisting of - chain contraction ``phi`` - chain complex `M` This construction appears in a paper by Pilarczyk and Réal [PR]_. Given a cell complex `K` and a field `F`, there is a chain complex `C` associated to `K` with coefficients in `F`. The *algebraic topological model* for `K` is a chain complex `M` with trivial differential, along with chain maps `\pi: C \to M` and `\iota: M \to C` such that - `\pi \iota = 1_M`, and - there is a chain homotopy `\phi` between `1_C` and `\iota \pi`. In particular, `\pi` and `\iota` induce isomorphisms on homology, and since `M` has trivial differential, it is its own homology, and thus also the homology of `C`. Thus `\iota` lifts homology classes to their cycle representatives. The chain homotopy `\phi` satisfies some additional properties, making it a *chain contraction*: - `\phi \phi = 0`, - `\pi \phi = 0`, - `\phi \iota = 0`. Given an algebraic topological model for `K`, it is then easy to compute cup products and cohomology operations on the cohomology of `K`, as described in [G-DR03]_ and [PR]_. Implementation details: the cell complex `K` must have an :meth:`~sage.homology.cell_complex.GenericCellComplex.n_cells` method from which we can extract a list of cells in each dimension. Combining the lists in increasing order of dimension then defines a filtration of the complex: a list of cells in which the boundary of each cell consists of cells earlier in the list. This is required by Pilarczyk and Réal's algorithm. There must also be a :meth:`~sage.homology.cell_complex.GenericCellComplex.chain_complex` method, to construct the chain complex `C` associated to this chain complex. In particular, this works for simplicial complexes and cubical complexes. It doesn't work for `\Delta`-complexes, though: the list of their `n`-cells has the wrong format. Note that from the chain contraction ``phi``, one can recover the chain maps `\pi` and `\iota` via ``phi.pi()`` and ``phi.iota()``. Then one can recover `C` and `M` from, for example, ``phi.pi().domain()`` and ``phi.pi().codomain()``, respectively. EXAMPLES:: sage: from sage.homology.algebraic_topological_model import algebraic_topological_model sage: RP2 = simplicial_complexes.RealProjectivePlane() sage: phi, M = algebraic_topological_model(RP2, GF(2)) sage: M.homology() {0: Vector space of dimension 1 over Finite Field of size 2, 1: Vector space of dimension 1 over Finite Field of size 2, 2: Vector space of dimension 1 over Finite Field of size 2} sage: T = cubical_complexes.Torus() sage: phi, M = algebraic_topological_model(T, QQ) sage: M.homology() {0: Vector space of dimension 1 over Rational Field, 1: Vector space of dimension 2 over Rational Field, 2: Vector space of dimension 1 over Rational Field} If you want to work with cohomology rather than homology, just dualize the outputs of this function:: sage: M.dual().homology() {0: Vector space of dimension 1 over Rational Field, 1: Vector space of dimension 2 over Rational Field, 2: Vector space of dimension 1 over Rational Field} sage: M.dual().degree_of_differential() 1 sage: phi.dual() Chain homotopy between: Chain complex endomorphism of Chain complex with at most 3 nonzero terms over Rational Field and Chain complex morphism: From: Chain complex with at most 3 nonzero terms over Rational Field To: Chain complex with at most 3 nonzero terms over Rational Field In degree 0, the inclusion of the homology `M` into the chain complex `C` sends the homology generator to a single vertex:: sage: K = simplicial_complexes.Simplex(2) sage: phi, M = algebraic_topological_model(K, QQ) sage: phi.iota().in_degree(0) [0] [0] [1] In cohomology, though, one needs the dual of every degree 0 cell to detect the degree 0 cohomology generator:: sage: phi.dual().iota().in_degree(0) [1] [1] [1] TESTS:: sage: T = cubical_complexes.Torus() sage: C = T.chain_complex() sage: H, M = T.algebraic_topological_model() sage: C.differential(1) * H.iota().in_degree(1).column(0) == 0 True sage: C.differential(1) * H.iota().in_degree(1).column(1) == 0 True sage: coC = T.chain_complex(cochain=True) sage: coC.differential(1) * H.dual().iota().in_degree(1).column(0) == 0 True sage: coC.differential(1) * H.dual().iota().in_degree(1).column(1) == 0 True """ if not base_ring.is_field(): raise ValueError('the coefficient ring must be a field') # The following are all dictionaries indexed by dimension. # For each n, gens[n] is an ordered list of the n-cells generating the complex M. gens = {} # For each n, phi_dict[n] is a dictionary of the form {idx: # vector}, where idx is the index of an n-cell in the list of # n-cells in K, and vector is the image of that n-cell, as an # element in the free module of (n+1)-chains for K. phi_dict = {} # For each n, pi_dict[n] is a dictionary of the same form, except # that the target vectors should be elements of the chain complex M. pi_dict = {} # For each n, iota_dict[n] is a dictionary of the form {cell: # vector}, where cell is one of the generators for M and vector is # its image in C, as an element in the free module of n-chains. iota_dict = {} for n in range(K.dimension()+1): gens[n] = [] phi_dict[n] = {} pi_dict[n] = {} iota_dict[n] = {} C = K.chain_complex(base_ring=base_ring) # old_cells: cells one dimension lower. old_cells = [] for dim in range(K.dimension()+1): n_cells = K.n_cells(dim) diff = C.differential(dim) # diff is sparse and low density. Dense matrices are faster # over finite fields, but for low density matrices, sparse # matrices are faster over the rationals. if base_ring != QQ: diff = diff.dense_matrix() rank = len(n_cells) old_rank = len(old_cells) V_old = VectorSpace(base_ring, old_rank) zero = V_old.zero_vector() for c_idx, c in enumerate(zip(n_cells, VectorSpace(base_ring, rank).gens())): # c is the pair (cell, the corresponding standard basis # vector in the free module of chains). Separate its # components, calling them c and c_vec: c_vec = c[1] c = c[0] # No need to set zero values for any of the maps: we will # assume any unset values are zero. # From the paper: phi_dict[c] = 0. # c_bar = c - phi(bdry(c)) c_bar = c_vec bdry_c = diff * c_vec # Apply phi to bdry_c and subtract from c_bar. for (idx, coord) in bdry_c.iteritems(): try: c_bar -= coord * phi_dict[dim-1][idx] except KeyError: pass bdry_c_bar = diff * c_bar # Evaluate pi(bdry(c_bar)). pi_bdry_c_bar = zero for (idx, coeff) in bdry_c_bar.iteritems(): try: pi_bdry_c_bar += coeff * pi_dict[dim-1][idx] except KeyError: pass # One small typo in the published algorithm: it says # "if bdry(c_bar) == 0", but should say # "if pi(bdry(c_bar)) == 0". if not pi_bdry_c_bar: # Append c to list of gens. gens[dim].append(c) # iota(c) = c_bar iota_dict[dim][c] = c_bar # pi(c) = c pi_dict[dim][c_idx] = c_vec else: # Take any u in gens so that lambda_i = <u, pi(bdry(c_bar))> != 0. # u_idx will be the index of the corresponding cell. for (u_idx, lambda_i) in pi_bdry_c_bar.iteritems(): # Now find the actual cell. u = old_cells[u_idx] if u in gens[dim-1]: break # pi(c) = 0: no need to do anything about this. for c_j_idx in range(old_rank): # eta_ij = <u, pi(c_j)>. try: eta_ij = pi_dict[dim-1][c_j_idx][u_idx] except (KeyError, IndexError): eta_ij = 0 if eta_ij: # Adjust phi(c_j). try: phi_dict[dim-1][c_j_idx] += eta_ij * lambda_i**(-1) * c_bar except KeyError: phi_dict[dim-1][c_j_idx] = eta_ij * lambda_i**(-1) * c_bar # Adjust pi(c_j). try: pi_dict[dim-1][c_j_idx] += -eta_ij * lambda_i**(-1) * pi_bdry_c_bar except KeyError: pi_dict[dim-1][c_j_idx] = -eta_ij * lambda_i**(-1) * pi_bdry_c_bar gens[dim-1].remove(u) del iota_dict[dim-1][u] old_cells = n_cells # Now we have constructed the raw data for M, pi, iota, phi, so we # have to convert that to data which can be used to construct chain # complexes, chain maps, and chain contractions. # M_data will contain (trivial) matrices defining the differential # on M. Keep track of the sizes using "M_rows" and "M_cols", which are # just the ranks of consecutive graded pieces of M. M_data = {} M_rows = 0 # pi_data: the matrices defining pi. Similar for iota_data and phi_data. pi_data = {} iota_data = {} phi_data = {} for n in range(K.dimension()+1): n_cells = K.n_cells(n) # Remove zero entries from pi_dict and phi_dict. pi_dict[n] = {i: pi_dict[n][i] for i in pi_dict[n] if pi_dict[n][i]} phi_dict[n] = {i: phi_dict[n][i] for i in phi_dict[n] if phi_dict[n][i]} # Convert gens to data defining the chain complex M with # trivial differential. M_cols = len(gens[n]) M_data[n] = zero_matrix(base_ring, M_rows, M_cols) M_rows = M_cols # Convert the dictionaries for pi, iota, phi to matrices which # will define chain maps and chain homotopies. pi_cols = [] phi_cols = [] for (idx, c) in enumerate(n_cells): # First pi: if idx in pi_dict[n]: column = vector(base_ring, M_rows) for (entry, coeff) in pi_dict[n][idx].iteritems(): # Translate from cells in n_cells to cells in gens[n]. column[gens[n].index(n_cells[entry])] = coeff else: column = vector(base_ring, M_rows) pi_cols.append(column) # Now phi: try: column = phi_dict[n][idx] except KeyError: column = vector(base_ring, len(K.n_cells(n+1))) phi_cols.append(column) # Now iota: iota_cols = [iota_dict[n][c] for c in gens[n]] pi_data[n] = matrix(base_ring, pi_cols).transpose() iota_data[n] = matrix(base_ring, len(gens[n]), len(n_cells), iota_cols).transpose() phi_data[n] = matrix(base_ring, phi_cols).transpose() M = ChainComplex(M_data, base_ring=base_ring, degree=-1) pi = ChainComplexMorphism(pi_data, C, M) iota = ChainComplexMorphism(iota_data, M, C) phi = ChainContraction(phi_data, pi, iota) return phi, M
def HughesPlane(q2, check=True): r""" Return the Hughes projective plane of order ``q2``. Let `q` be an odd prime, the Hughes plane of order `q^2` is a finite projective plane of order `q^2` introduced by D. Hughes in [Hu57]_. Its construction is as follows. Let `K = GF(q^2)` be a finite field with `q^2` elements and `F = GF(q) \subset K` be its unique subfield with `q` elements. We define a twisted multiplication on `K` as .. MATH:: x \circ y = \begin{cases} x\ y & \text{if y is a square in K}\\ x^q\ y & \text{otherwise} \end{cases} The points of the Hughes plane are the triples `(x, y, z)` of points in `K^3 \backslash \{0,0,0\}` up to the equivalence relation `(x,y,z) \sim (x \circ k, y \circ k, z \circ k)` where `k \in K`. For `a = 1` or `a \in (K \backslash F)` we define a block `L(a)` as the set of triples `(x,y,z)` so that `x + a \circ y + z = 0`. The rest of the blocks are obtained by letting act the group `GL(3, F)` by its standard action. For more information, see :wikipedia:`Hughes_plane` and [We07]. .. SEEALSO:: :func:`DesarguesianProjectivePlaneDesign` to build the Desarguesian projective planes INPUT: - ``q2`` -- an even power of an odd prime number - ``check`` -- (boolean) Whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. EXAMPLES:: sage: H = designs.HughesPlane(9) sage: H (91,10,1)-Balanced Incomplete Block Design We prove in the following computations that the Desarguesian plane ``H`` is not Desarguesian. Let us consider the two triangles `(0,1,10)` and `(57, 70, 59)`. We show that the intersection points `D_{0,1} \cap D_{57,70}`, `D_{1,10} \cap D_{70,59}` and `D_{10,0} \cap D_{59,57}` are on the same line while `D_{0,70}`, `D_{1,59}` and `D_{10,57}` are not concurrent:: sage: blocks = H.blocks() sage: line = lambda p,q: next(b for b in blocks if p in b and q in b) sage: b_0_1 = line(0, 1) sage: b_1_10 = line(1, 10) sage: b_10_0 = line(10, 0) sage: b_57_70 = line(57, 70) sage: b_70_59 = line(70, 59) sage: b_59_57 = line(59, 57) sage: set(b_0_1).intersection(b_57_70) {2} sage: set(b_1_10).intersection(b_70_59) {73} sage: set(b_10_0).intersection(b_59_57) {60} sage: line(2, 73) == line(73, 60) True sage: b_0_57 = line(0, 57) sage: b_1_70 = line(1, 70) sage: b_10_59 = line(10, 59) sage: p = set(b_0_57).intersection(b_1_70) sage: q = set(b_1_70).intersection(b_10_59) sage: p == q False TESTS: Some wrong input:: sage: designs.HughesPlane(5) Traceback (most recent call last): ... EmptySetError: No Hughes plane of non-square order exists. sage: designs.HughesPlane(16) Traceback (most recent call last): ... EmptySetError: No Hughes plane of even order exists. Check that it works for non-prime `q`:: sage: designs.HughesPlane(3**4) # not tested - 10 secs (6643,82,1)-Balanced Incomplete Block Design """ if not q2.is_square(): raise EmptySetError("No Hughes plane of non-square order exists.") if q2 % 2 == 0: raise EmptySetError("No Hughes plane of even order exists.") q = q2.sqrt() K = FiniteField(q2, prefix='x') F = FiniteField(q, prefix='y') A = q3_minus_one_matrix(F) A = A.change_ring(K) m = K.list() V = VectorSpace(K, 3) zero = K.zero() one = K.one() points = [(x, y, one) for x in m for y in m] + \ [(x, one, zero) for x in m] + \ [(one, zero, zero)] relabel = {tuple(p): i for i, p in enumerate(points)} blcks = [] for a in m: if a not in F or a == 1: # build L(a) aa = ~a l = [] l.append(V((-a, one, zero))) for x in m: y = -aa * (x + one) if not y.is_square(): y *= aa**(q - 1) l.append(V((x, y, one))) # compute the orbit of L(a) blcks.append( [relabel[normalize_hughes_plane_point(p, q)] for p in l]) for i in range(q2 + q): l = [A * j for j in l] blcks.append( [relabel[normalize_hughes_plane_point(p, q)] for p in l]) from .bibd import BalancedIncompleteBlockDesign return BalancedIncompleteBlockDesign(q2**2 + q2 + 1, blcks, check=check)
def _possible_normalizers(E, SA): r"""Find a list containing all primes `l` such that the Galois image at `l` is contained in the normalizer of a Cartan subgroup, such that the corresponding quadratic character is ramified only at the given primes. INPUT: - ``E`` - EllipticCurve - over a number field K. - ``SA`` - list - a list of primes of K. OUTPUT: - list - A list of primes, which contains all primes `l` such that the Galois image at `l` is contained in the normalizer of a Cartan subgroup, such that the corresponding quadratic character is ramified only at primes in SA. - If `E` has geometric CM that is not defined over its ground field, a ValueError is raised. EXAMPLES:: sage: E = EllipticCurve([0,0,0,-56,4848]) sage: 5 in sage.schemes.elliptic_curves.gal_reps_number_field._possible_normalizers(E, [ZZ.ideal(2)]) True """ E = _over_numberfield(E) K = E.base_field() SA = [K.ideal(I.gens()) for I in SA] x = K['x'].gen() selmer_group = K.selmer_group(SA, 2) # Generators of the selmer group. if selmer_group == []: return [] V = VectorSpace(GF(2), len(selmer_group)) # We think of this as the character group of the selmer group. traces_list = [] W = V.zero_subspace() deg_one_primes = K.primes_of_degree_one_iter() while W.dimension() < V.dimension() - 1: P = deg_one_primes.next() k = P.residue_field() defines_valid_character = True # A prime P defines a quadratic residue character # on the Selmer group. This variable will be set # to zero if any elements of the selmer group are # zero mod P (i.e. the character is ramified). splitting_vector = [] # This will be the values of this # character on the generators of the Selmer group. for a in selmer_group: abar = k(a) if abar == 0: # Ramification. defines_valid_character = False break if abar.is_square(): splitting_vector.append(GF(2)(0)) else: splitting_vector.append(GF(2)(1)) if not defines_valid_character: continue if splitting_vector in W: continue try: Etilde = E.change_ring(k) except ArithmeticError: # Bad reduction. continue tr = Etilde.trace_of_frobenius() if tr == 0: continue traces_list.append(tr) W = W + V.span([splitting_vector]) bad_primes = set([]) for i in traces_list: for p in i.prime_factors(): bad_primes.add(p) # We find the unique vector v in V orthogonal to W: v = W.matrix().transpose().kernel().basis()[0] # We find the element a of the selmer group corresponding to v: a = 1 for i in xrange(len(selmer_group)): if v[i] == 1: a *= selmer_group[i] # Since we've already included the above bad primes, we can assume # that the quadratic character corresponding to the exceptional primes # we're looking for is given by mapping into Gal(K[\sqrt{a}]/K). patience = 5 * K.degree() # Number of Frobenius elements to check before suspecting that E # has CM and computing the set of CM j-invariants of K to check. # TODO: Is this the best value for this parameter? while True: P = deg_one_primes.next() k = P.residue_field() if not k(a).is_square(): try: tr = E.change_ring(k).trace_of_frobenius() except ArithmeticError: # Bad reduction. continue if tr == 0: patience -= 1 if patience == 0: # We suspect E has CM, so we check: if E.j_invariant() in cm_j_invariants(K): raise ValueError("The curve E should not have CM.") else: for p in tr.prime_factors(): bad_primes.add(p) bad_primes = sorted(bad_primes) return bad_primes
def __init__(self, base_field): self._f=base_field # The vector space of vectors self._vs = VectorSpace(self._f,2) Group.__init__(self, category=Groups().Infinite())
class SimilarityGroup(UniqueRepresentation,Group): r'''Group representing all similarities in the plane. This is the group generated by rotations, translations and dilations. ''' Element = Similarity def _element_constructor_(self, *args, **kwds): if len(args)!=1: return self.element_class(self, *args, **kwds) x = args[0] p=parent(x) if self._f.has_coerce_map_from(p): return self.element_class( self,self._f(x), self._f.zero(), self._f.zero(), self._f.zero()) if isinstance(p, SimilarityGroup): return self.element_class(self, x.a(), x.b(), x.s(), x.t()) if isinstance(p, TranslationGroup): return self.element_class( self,self._f.one(), self._f.zero(), x.s(), x.t() ) return self.element_class(self, x, **kwds) def _coerce_map_from_(self, S): if self._f.has_coerce_map_from(S): return True if isinstance(S, SimilarityGroup): return self._f.has_coerce_map_from(S._f) if isinstance(S, TranslationGroup): return self._f.has_coerce_map_from(S.base_field()) def __init__(self, base_field): self._f=base_field # The vector space of vectors self._vs = VectorSpace(self._f,2) Group.__init__(self, category=Groups().Infinite()) def _repr_(self): return "SimilarityGroup over field "+str(self._f) def one(self): return self.element_class(self,self._f.one(),self._f.zero(),self._f.zero(),self._f.zero()) def an_element(self): return self.element_class(self,self._f(ZZ_3),self._f(ZZ_4),self._f(ZZ_2),self._f(-ZZ_1)) def is_abelian(self): return False def gens(self): pairs=[ (self._f.one(),self._f.zero()), (self._f(ZZ_2),self._f.zero()), (self._f.zero(),self._f(ZZ_2)), (self._f(ZZ_3),self._f(ZZ_4))] l=[] for p in pairs: for v in self._vs.gens(): l.append(self.element_class(self,p[0],p[1],v[0],v[1])) return l # For pickling: #def __reduce__(self): # return self.__class__, (self._f,) #def _cmp_(self, other): # return self._f == other._f #__cmp__=_cmp_ def base_field(self): return self._f
def test_full(self): v_space = VectorSpace(QQ,4) zero = v_space.subspace([v_space.zero()]) comp = orth_complement(v_space,v_space) self.assertEqual(zero,comp)
def test_comp(self): v_space = VectorSpace(QQ,4) sub = v_space.subspace([[1,1,1,1],[2,2,-3,-1]]) comp = orth_complement(v_space,sub) true_comp = v_space.subspace([[19,-17,3,-5],[1,-1,0,0]]) self.assertEqual(comp,true_comp)
def linear_transformation(arg0, arg1=None, arg2=None, side='left'): r""" Create a linear transformation from a variety of possible inputs. FORMATS: In the following, ``D`` and ``C`` are vector spaces over the same field that are the domain and codomain (respectively) of the linear transformation. ``side`` is a keyword that is either 'left' or 'right'. When a matrix is used to specify a linear transformation, as in the first two call formats below, you may specify if the function is given by matrix multiplication with the vector on the left, or the vector on the right. The default is 'left'. Internally representations are always carried as the 'left' version, and the default text representation is this version. However, the matrix representation may be obtained as either version, no matter how it is created. - ``linear_transformation(A, side='left')`` Where ``A`` is a matrix. The domain and codomain are inferred from the dimension of the matrix and the base ring of the matrix. The base ring must be a field, or have its fraction field implemented in Sage. - ``linear_transformation(D, C, A, side='left')`` ``A`` is a matrix that behaves as above. However, now the domain and codomain are given explicitly. The matrix is checked for compatibility with the domain and codomain. Additionally, the domain and codomain may be supplied with alternate ("user") bases and the matrix is interpreted as being a representation relative to those bases. - ``linear_transformation(D, C, f)`` ``f`` is any function that can be applied to the basis elements of the domain and that produces elements of the codomain. The linear transformation returned is the unique linear transformation that extends this mapping on the basis elements. ``f`` may come from a function defined by a Python ``def`` statement, or may be defined as a ``lambda`` function. Alternatively, ``f`` may be specified by a callable symbolic function, see the examples below for a demonstration. - ``linear_transformation(D, C, images)`` ``images`` is a list, or tuple, of codomain elements, equal in number to the size of the basis of the domain. Each basis element of the domain is mapped to the corresponding element of the ``images`` list, and the linear transformation returned is the unique linear transfromation that extends this mapping. OUTPUT: A linear transformation described by the input. This is a "vector space morphism", an object of the class :class:`sage.modules.vector_space_morphism`. EXAMPLES: We can define a linear transformation with just a matrix, understood to act on a vector placed on one side or the other. The field for the vector spaces used as domain and codomain is obtained from the base ring of the matrix, possibly promoting to a fraction field. :: sage: A = matrix(ZZ, [[1, -1, 4], [2, 0, 5]]) sage: phi = linear_transformation(A) sage: phi Vector space morphism represented by the matrix: [ 1 -1 4] [ 2 0 5] Domain: Vector space of dimension 2 over Rational Field Codomain: Vector space of dimension 3 over Rational Field sage: phi([1/2, 5]) (21/2, -1/2, 27) sage: B = matrix(Integers(7), [[1, 2, 1], [3, 5, 6]]) sage: rho = linear_transformation(B, side='right') sage: rho Vector space morphism represented by the matrix: [1 3] [2 5] [1 6] Domain: Vector space of dimension 3 over Ring of integers modulo 7 Codomain: Vector space of dimension 2 over Ring of integers modulo 7 sage: rho([2, 4, 6]) (2, 6) We can define a linear transformation with a matrix, while explicitly giving the domain and codomain. Matrix entries will be coerced into the common field of scalars for the vector spaces. :: sage: D = QQ^3 sage: C = QQ^2 sage: A = matrix([[1, 7], [2, -1], [0, 5]]) sage: A.parent() Full MatrixSpace of 3 by 2 dense matrices over Integer Ring sage: zeta = linear_transformation(D, C, A) sage: zeta.matrix().parent() Full MatrixSpace of 3 by 2 dense matrices over Rational Field sage: zeta Vector space morphism represented by the matrix: [ 1 7] [ 2 -1] [ 0 5] Domain: Vector space of dimension 3 over Rational Field Codomain: Vector space of dimension 2 over Rational Field Matrix representations are relative to the bases for the domain and codomain. :: sage: u = vector(QQ, [1, -1]) sage: v = vector(QQ, [2, 3]) sage: D = (QQ^2).subspace_with_basis([u, v]) sage: x = vector(QQ, [2, 1]) sage: y = vector(QQ, [-1, 4]) sage: C = (QQ^2).subspace_with_basis([x, y]) sage: A = matrix(QQ, [[2, 5], [3, 7]]) sage: psi = linear_transformation(D, C, A) sage: psi Vector space morphism represented by the matrix: [2 5] [3 7] Domain: Vector space of degree 2 and dimension 2 over Rational Field User basis matrix: [ 1 -1] [ 2 3] Codomain: Vector space of degree 2 and dimension 2 over Rational Field User basis matrix: [ 2 1] [-1 4] sage: psi(u) == 2*x + 5*y True sage: psi(v) == 3*x + 7*y True Functions that act on the domain may be used to compute images of the domain's basis elements, and this mapping can be extended to a unique linear transformation. The function may be a Python function (via ``def`` or ``lambda``) or a Sage symbolic function. :: sage: def g(x): ... return vector(QQ, [2*x[0]+x[2], 5*x[1]]) ... sage: phi = linear_transformation(QQ^3, QQ^2, g) sage: phi Vector space morphism represented by the matrix: [2 0] [0 5] [1 0] Domain: Vector space of dimension 3 over Rational Field Codomain: Vector space of dimension 2 over Rational Field sage: f = lambda x: vector(QQ, [2*x[0]+x[2], 5*x[1]]) sage: rho = linear_transformation(QQ^3, QQ^2, f) sage: rho Vector space morphism represented by the matrix: [2 0] [0 5] [1 0] Domain: Vector space of dimension 3 over Rational Field Codomain: Vector space of dimension 2 over Rational Field sage: x, y, z = var('x y z') sage: h(x, y, z) = [2*x + z, 5*y] sage: zeta = linear_transformation(QQ^3, QQ^2, h) sage: zeta Vector space morphism represented by the matrix: [2 0] [0 5] [1 0] Domain: Vector space of dimension 3 over Rational Field Codomain: Vector space of dimension 2 over Rational Field sage: phi == rho True sage: rho == zeta True We create a linear transformation relative to non-standard bases, and capture its representation relative to standard bases. With this, we can build functions that create the same linear transformation relative to the nonstandard bases. :: sage: u = vector(QQ, [1, -1]) sage: v = vector(QQ, [2, 3]) sage: D = (QQ^2).subspace_with_basis([u, v]) sage: x = vector(QQ, [2, 1]) sage: y = vector(QQ, [-1, 4]) sage: C = (QQ^2).subspace_with_basis([x, y]) sage: A = matrix(QQ, [[2, 5], [3, 7]]) sage: psi = linear_transformation(D, C, A) sage: rho = psi.restrict_codomain(QQ^2).restrict_domain(QQ^2) sage: rho.matrix() [ -4/5 97/5] [ 1/5 -13/5] sage: f = lambda x: vector(QQ, [(-4/5)*x[0] + (1/5)*x[1], (97/5)*x[0] + (-13/5)*x[1]]) sage: psi = linear_transformation(D, C, f) sage: psi.matrix() [2 5] [3 7] sage: s, t = var('s t') sage: h(s, t) = [(-4/5)*s + (1/5)*t, (97/5)*s + (-13/5)*t] sage: zeta = linear_transformation(D, C, h) sage: zeta.matrix() [2 5] [3 7] Finally, we can give an explicit list of images for the basis elements of the domain. :: sage: x = polygen(QQ) sage: F.<a> = NumberField(x^3+x+1) sage: u = vector(F, [1, a, a^2]) sage: v = vector(F, [a, a^2, 2]) sage: w = u + v sage: D = F^3 sage: C = F^3 sage: rho = linear_transformation(D, C, [u, v, w]) sage: rho.matrix() [ 1 a a^2] [ a a^2 2] [ a + 1 a^2 + a a^2 + 2] sage: C = (F^3).subspace_with_basis([u, v]) sage: D = (F^3).subspace_with_basis([u, v]) sage: psi = linear_transformation(C, D, [u+v, u-v]) sage: psi.matrix() [ 1 1] [ 1 -1] TESTS: We test some bad inputs. First, the wrong things in the wrong places. :: sage: linear_transformation('junk') Traceback (most recent call last): ... TypeError: first argument must be a matrix or a vector space, not junk sage: linear_transformation(QQ^2, QQ^3, 'stuff') Traceback (most recent call last): ... TypeError: third argument must be a matrix, function, or list of images, not stuff sage: linear_transformation(QQ^2, 'garbage') Traceback (most recent call last): ... TypeError: if first argument is a vector space, then second argument must be a vector space, not garbage sage: linear_transformation(QQ^2, Integers(7)^2) Traceback (most recent call last): ... TypeError: vector spaces must have the same field of scalars, not Rational Field and Ring of integers modulo 7 Matrices must be over a field (or a ring that can be promoted to a field), and of the right size. :: sage: linear_transformation(matrix(Integers(6), [[2, 3],[4, 5]])) Traceback (most recent call last): ... TypeError: matrix must have entries from a field, or a ring with a fraction field, not Ring of integers modulo 6 sage: A = matrix(QQ, 3, 4, range(12)) sage: linear_transformation(QQ^4, QQ^4, A) Traceback (most recent call last): ... TypeError: domain dimension is incompatible with matrix size sage: linear_transformation(QQ^3, QQ^3, A, side='right') Traceback (most recent call last): ... TypeError: domain dimension is incompatible with matrix size sage: linear_transformation(QQ^3, QQ^3, A) Traceback (most recent call last): ... TypeError: codomain dimension is incompatible with matrix size sage: linear_transformation(QQ^4, QQ^4, A, side='right') Traceback (most recent call last): ... TypeError: codomain dimension is incompatible with matrix size Lists of images can be of the wrong number, or not really elements of the codomain. :: sage: linear_transformation(QQ^3, QQ^2, [vector(QQ, [1,2])]) Traceback (most recent call last): ... ValueError: number of images should equal the size of the domain's basis (=3), not 1 sage: C = (QQ^2).subspace_with_basis([vector(QQ, [1,1])]) sage: linear_transformation(QQ^1, C, [vector(QQ, [1,2])]) Traceback (most recent call last): ... ArithmeticError: some proposed image is not in the codomain, because element [1, 2] is not in free module Functions may not apply properly to domain elements, or return values outside the codomain. :: sage: f = lambda x: vector(QQ, [x[0], x[4]]) sage: linear_transformation(QQ^3, QQ^2, f) Traceback (most recent call last): ... ValueError: function cannot be applied properly to some basis element because vector index out of range sage: f = lambda x: vector(QQ, [x[0], x[1]]) sage: C = (QQ^2).span([vector(QQ, [1, 1])]) sage: linear_transformation(QQ^2, C, f) Traceback (most recent call last): ... ArithmeticError: some image of the function is not in the codomain, because element [1, 0] is not in free module A Sage symbolic function can come in a variety of forms that are not representative of a linear transformation. :: sage: x, y = var('x, y') sage: f(x, y) = [y, x, y] sage: linear_transformation(QQ^3, QQ^3, f) Traceback (most recent call last): ... ValueError: symbolic function has the wrong number of inputs for domain sage: linear_transformation(QQ^2, QQ^2, f) Traceback (most recent call last): ... ValueError: symbolic function has the wrong number of outputs for codomain sage: x, y = var('x y') sage: f(x, y) = [y, x*y] sage: linear_transformation(QQ^2, QQ^2, f) Traceback (most recent call last): ... ValueError: symbolic function must be linear in all the inputs: unable to convert y to a rational sage: x, y = var('x y') sage: f(x, y) = [x, 2*y] sage: C = (QQ^2).span([vector(QQ, [1, 1])]) sage: linear_transformation(QQ^2, C, f) Traceback (most recent call last): ... ArithmeticError: some image of the function is not in the codomain, because element [1, 0] is not in free module """ from sage.matrix.constructor import matrix from sage.modules.module import is_VectorSpace from sage.modules.free_module import VectorSpace from sage.categories.homset import Hom from sage.symbolic.ring import SymbolicRing from sage.modules.vector_callable_symbolic_dense import Vector_callable_symbolic_dense from inspect import isfunction if not side in ['left', 'right']: raise ValueError("side must be 'left' or 'right', not {0}".format(side)) if not (is_Matrix(arg0) or is_VectorSpace(arg0)): raise TypeError('first argument must be a matrix or a vector space, not {0}'.format(arg0)) if is_Matrix(arg0): R = arg0.base_ring() if not R.is_field(): try: R = R.fraction_field() except (NotImplementedError, TypeError): msg = 'matrix must have entries from a field, or a ring with a fraction field, not {0}' raise TypeError(msg.format(R)) if side == 'right': arg0 = arg0.transpose() side = 'left' arg2 = arg0 arg0 = VectorSpace(R, arg2.nrows()) arg1 = VectorSpace(R, arg2.ncols()) elif is_VectorSpace(arg0): if not is_VectorSpace(arg1): msg = 'if first argument is a vector space, then second argument must be a vector space, not {0}' raise TypeError(msg.format(arg1)) if arg0.base_ring() != arg1.base_ring(): msg = 'vector spaces must have the same field of scalars, not {0} and {1}' raise TypeError(msg.format(arg0.base_ring(), arg1.base_ring())) # Now arg0 = domain D, arg1 = codomain C, and # both are vector spaces with common field of scalars # use these to make a VectorSpaceHomSpace # arg2 might be a matrix that began in arg0 D = arg0 C = arg1 H = Hom(D, C, category=None) # Examine arg2 as the "rule" for the linear transformation # Pass on matrices, Python functions and lists to homspace call # Convert symbolic function here, to a matrix if is_Matrix(arg2): if side == 'right': arg2 = arg2.transpose() elif isinstance(arg2, (list, tuple)): pass elif isfunction(arg2): pass elif isinstance(arg2, Vector_callable_symbolic_dense): args = arg2.parent().base_ring()._arguments exprs = arg2.change_ring(SymbolicRing()) m = len(args) n = len(exprs) if m != D.degree(): raise ValueError('symbolic function has the wrong number of inputs for domain') if n != C.degree(): raise ValueError('symbolic function has the wrong number of outputs for codomain') arg2 = [[e.coefficient(a) for e in exprs] for a in args] try: arg2 = matrix(D.base_ring(), m, n, arg2) except TypeError as e: msg = 'symbolic function must be linear in all the inputs:\n' + e.args[0] raise ValueError(msg) # have matrix with respect to standard bases, now consider user bases images = [v*arg2 for v in D.basis()] try: arg2 = matrix([C.coordinates(C(a)) for a in images]) except (ArithmeticError, TypeError) as e: msg = 'some image of the function is not in the codomain, because\n' + e.args[0] raise ArithmeticError(msg) else: msg = 'third argument must be a matrix, function, or list of images, not {0}' raise TypeError(msg.format(arg2)) # arg2 now compatible with homspace H call method # __init__ will check matrix sizes versus domain/codomain dimensions return H(arg2)
def test_satisfy_sum(self): v_space = VectorSpace(QQ,4) sub = v_space.subspace([[1,1,1,-1],[2,-3,41,5]]) comp = orth_complement(v_space,sub) self.assertEqual(v_space,sub+comp)