def path_is_unicolor(self, path): r""" Return if the edges of the path have the same color. """ edges = Set([Set(e) for e in zip(path[:-1], path[1:])]) return edges.issubset(self._red_edges) or edges.issubset( self._blue_edges)
def _find_components_orbits(self): red_subgraph = self.red_subgraph() blue_subgraph = self.blue_subgraph() self._partially_invariant_components = {} self._noninvariant_components = {} self._partially_invariant_orbits = {} self._noninvariant_orbits = {} for one_color_subgraph, col in [[red_subgraph, 'red'], [blue_subgraph, 'blue']]: invariant_comps = [] noninv_comps = [] comps_as_sets = [ Set(component) for component in one_color_subgraph.connected_components() ] comps_perm = PermutationGroup([[ Set([self.omega(v) for v in component]) for component in comps_as_sets ]], domain=comps_as_sets) for orbit in comps_perm.orbits(): if len(orbit) < self.n: invariant_comps.append(orbit) else: noninv_comps.append(orbit) self._partially_invariant_orbits[col] = invariant_comps self._noninvariant_orbits[col] = noninv_comps self._partially_invariant_components[col] = [ list(comp) for orbit in invariant_comps for comp in orbit ] self._noninvariant_components[col] = [ list(comp) for orbit in noninv_comps for comp in orbit ]
def find_axes2(nodeSet, candidates): """ Returns all possible coherent axes :param node: Current node ([ballot_set], upper_bound) :param candidates: List of candidates :return: List of possible axes, False if none found """ L = [] ballots = transform_ballots(nodeSet) # Regrouper les bulletins qui contiennent le candidat c for c in candidates: S = Set([]) for ballot in ballots: if c in ballot: S += Set([ballot]) S += Set([Set([c])]) L += Set([S]) # Transformer liste de Set en PQ-tree et aligner axes = P(L) for ballot in ballots: try: axes.set_contiguous(ballot) except: return False, 0 # Determiner les axes à partir des alignements trouvés all_axes = [] # Liste des axes for axis in axes.orderings(): A = [] for ballot_set in flatten(axis): A += [L.index(ballot_set)+1] all_axes += [A] axes_filtered = filter_symmetric_axes(all_axes) return axes_filtered, axes.cardinality()
def is_equal(self, other_coloring, moduloConjugation=True): r""" Return if the NAC-coloring is equal to ``other_coloring``. INPUT: - ``moduloConjugation`` -- If ``True`` (default), then the NAC-colorings are compared modulo swapping colors. EXAMPLES:: sage: from flexrilog import NACcoloring, GraphGenerator sage: G = GraphGenerator.SmallestFlexibleLamanGraph(); G SmallestFlexibleLamanGraph: FlexRiGraph with the vertices [0, 1, 2, 3, 4] and edges [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 4), (3, 4)] sage: delta1 = NACcoloring(G,[[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3)], [(2, 4), (3, 4)]]) sage: delta2 = NACcoloring(G,[[(2, 4), (3, 4)],[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3)]]) sage: delta1.is_equal(delta2) True sage: delta1.is_equal(delta2, moduloConjugation=False) False """ if moduloConjugation: return Set([self._red_edges, self._blue_edges]) == Set( [other_coloring._red_edges, other_coloring._blue_edges]) else: return self._red_edges == other_coloring._red_edges and self._blue_edges == other_coloring._blue_edges
def color(self, u, v=None): r""" Return the color of an edge. INPUT: If ``v`` is ``None``, then ``u`` is consider to be an edge. Otherwise, ``uv`` is taken as the edge. EXAMPLES:: sage: from flexrilog import FlexRiGraph sage: G = FlexRiGraph(graphs.CompleteBipartiteGraph(3,3)) sage: delta = G.NAC_colorings()[0] sage: delta.color(0,3) 'red' sage: delta.color([2,4]) 'blue' sage: delta.color(1,2) Traceback (most recent call last): ... ValueError: There is no edge [1, 2] """ if not v is None: if not self._graph.has_edge(u, v): raise exceptions.ValueError('There is no edge ' + str([u, v])) return 'red' if Set([u, v]) in self._red_edges else 'blue' else: if not self._graph.has_edge(u[0], u[1]): raise exceptions.ValueError('There is no edge ' + str([u[0], u[1]])) return 'red' if Set([u[0], u[1]]) in self._red_edges else 'blue'
def is_blue(self, u, v=None): r""" Return if the edge is blue. INPUT: If ``v`` is ``None``, then ``u`` is consider to be an edge. Otherwise, ``uv`` is taken as the edge. EXAMPLES:: sage: from flexrilog import FlexRiGraph sage: G = FlexRiGraph(graphs.CompleteBipartiteGraph(3,3)) sage: delta = G.NAC_colorings()[0] sage: delta.is_blue(2,4) True sage: delta.is_blue([0,4]) False sage: delta.is_blue(1,2) Traceback (most recent call last): ... ValueError: There is no edge [1, 2] """ if v: if not self._graph.has_edge(u, v): raise exceptions.ValueError('There is no edge ' + str([u, v])) return Set([u, v]) in self._blue_edges else: if not self._graph.has_edge(u[0], u[1]): raise exceptions.ValueError('There is no edge ' + str([u[0], u[1]])) return Set([u[0], u[1]]) in self._blue_edges
def sol(node, candidates, remaining_prefs): """ Calculates a local optimal solution :param v: :param node: Current node :param candidates: List of candidates :param remaining_prefs: List of remaining ballots in preferances :param axes: Compatible axes found at node :return: Value of local optimal solution """ v = 0 for (nb_voters, prefs) in node[0]: v += nb_voters for (nb_voters, prefs) in remaining_prefs: if isinstance(prefs[-1], int): ballot = Set(prefs) else: ballot = Set(prefs[:-1]) axes, nb_axes = find_axes2(node[0], candidates) for axis in axes: if is_coherent(ballot, axis): v += nb_voters node = (node[0] + [(nb_voters, prefs)], node[1]) break return (node, v)
def build_next(A, S, HYP, LIN): from sage.all import exists, Set new_level = [] new_hypcont = [] if len(S) > 0: m = S[0] for i in S: T = LIN[i - m] for j in range(len(A)): # Skip the hyperplane already known to contain the intersection. if not j in HYP[i - m]: H = A[j] I = H._affine_subspace().intersection(T) # Check if the intersection is trivial. if I is not None: if I == T: # This case means that H cap T = T, so we should # record that H contains T. HYP[i - m] = HYP[i - m].union(Set([j])) else: # Check if we have this intersection already. is_in, ind = exists(range(len(new_level)), lambda k: I == new_level[k]) if is_in: # We have the intersection, so we update # containment info accordingly. new_hypcont[ind] = new_hypcont[ind].union( Set([j]).union(HYP[i - m])) else: # We do not have it, so we update everything. new_level.append(I) new_hypcont.append(HYP[i - m].union(Set([j]))) return list(zip(new_level, new_hypcont))
def upper_bound(nodeSet, remaining_prefs, candidates, axes): """ Calculates the node's upper bound :param nodeSet: Current node set of prefs :param remaining_prefs: List of remaining ballots in preferences :param candidates: List of candidates :param axes: Compatible axes found at node :return: int """ new_bound = 0 for pref in nodeSet: new_bound += pref[0] for nb_voters, pref in remaining_prefs: # Transform preference to set of ballot without indifference if isinstance(pref[-1], int): ballot = Set(pref) else: ballot = Set(pref[:-1]) # Determine whether the ballot is coherent with one of the axes for axis in axes: if is_coherent(ballot, axis): # If coherent, add ballot number of voters to the new bound new_bound += nb_voters break return new_bound
def cycle_edges(cycle, sets=False): r""" Return edges of a 4-cycle. """ if sets: return Set([Set(list(e)) for e in zip(cycle, list(cycle[1:])+[cycle[0]])]) else: return [list(e) for e in zip(cycle, list(cycle[1:])+[cycle[0]])]
def last_adj(S): l_hyp = labels[1:] T = S.difference(labels[0]) new_T = Set([]) for k in range(len(l_hyp)): if l_hyp[k].issubset(T): new_T = new_T.union(Set([new_hyp[k]])) return new_T
def _check_edges(self): r""" Raise a ``RuntimeError`` if the edges of the NAC-coloring do not match the edges of the graph. """ if (Set([Set(e) for e in self._graph.edges(labels=False)]) != self._blue_edges.union(self._red_edges)): raise exceptions.RuntimeError( 'The edges of the NAC-coloring do not match the edges of the graph.' )
def _subposet(P, x, F): from sage.all import Set, Poset elts = Set([]) new_level = Set([x]) while len(elts.union(new_level)) > len(elts): elts = elts.union(new_level) new_level = Set(_reduce(lambda x, y: x + y, map(F, new_level), [])) new_P = P.subposet(elts) return new_P
def _get_labels(M, x, rows, L): from sage.all import VectorSpace, Set, Matrix # If non-central, then this is true iff not intersecting. not_e1 = lambda v: list(v)[1:] != [0] * (len(v) - 1) # Determine new hyperplanes and group like rows together. V = VectorSpace(M.base_ring(), M.ncols()) lines = [] labels = [] for r in range(M.nrows()): v = M[r] is_new = not_e1(v) i = 0 while i < len(lines) and is_new: if v in V.subspace([lines[i]]): is_new = False labels[i] = labels[i].union(Set([r])) else: i += 1 if is_new: lines.append(v) labels.append(Set([r])) # Adjust the labels because we are missing rows. fix_sets = lambda F: lambda S: Set(list(map(lambda s: F(s), S))) for k in rows: adjust = lambda i: i + (k <= i) * 1 labels = list(map(fix_sets(adjust), labels)) labels = [Set(rows)] + labels # Adjust the row labels to hyperplane labels HL = L.hyperplane_labels A = L.hyperplane_arrangement HL_lab = lambda i: list(filter(lambda j: HL[j] == A[i], HL.keys()))[0] labels = list(map(fix_sets(HL_lab), labels)) # Get the new hyperplanes FL = L.flat_labels new_hyp = [labels[k].union(labels[0]) for k in range(1, len(labels))] P = L.poset flat = lambda S: list(filter(lambda j: FL[j] == S, P.upper_covers(x)))[0] new_hyp = list(map(flat, new_hyp)) def last_adj(S): l_hyp = labels[1:] T = S.difference(labels[0]) new_T = Set([]) for k in range(len(l_hyp)): if l_hyp[k].issubset(T): new_T = new_T.union(Set([new_hyp[k]])) return new_T return Matrix(lines), last_adj, { new_hyp[i]: i for i in range(len(new_hyp)) }
def simplex_degree(s): deg = Set(edges) for u in s: if u in 'HUY': deg = deg.difference(Set([(1, 2)])) if u in 'HSZ': deg = deg.difference(Set([(1, 3)])) if u in 'HTX': deg = deg.difference(Set([(2, 3)])) return Set([e for e in edges if e in deg])
def simple_dict(self): r""" Return a dictionary containing the $k$-simple finite quadratic modules labeled by their level. """ d = {} if self._simple != None: for s in Set(self._simple): d[s.level()] = list() for s in Set(self._simple): d[s.level()].append(s) return d
def primes_iter(K, condition=None, sort_key=prime_label, maxnorm=Infinity): """Iterator through primes of K, sorted using the provided sort key, optionally with an upper bound on the norm. If condition is not None it should be a True/False function on rational primes, in which case only primes P dividing p for which condition(p) holds will be returned. For example, condition=lambda:not p.divides(6). """ # print("starting primes_iter with K={}, maxnorm={}".format(K,maxnorm)) # The set of possible degrees f of primes is the set of cycle # lengths in the Galois group acting as permutations on the roots # of the defining polynomial: dlist = Set([1, 2]) if K.degree() == 2 else Set( sum([list(g.cycle_type()) for g in K.galois_group()], [])) # Create an array of iterators, one for each residue degree PPs = [ primes_of_degree_iter(K, d, condition, sort_key, maxnorm=maxnorm) for d in dlist ] # pop the first prime off each iterator (allowing for the # possibility that there may be none): Ps = [0 for _ in dlist] ns = [Infinity for _ in dlist] for i, PP in enumerate(PPs): try: P = next(PP) Ps[i] = P ns[i] = P.norm() except StopIteration: pass while True: # find the smallest prime not yet popped; stop if this (hence # all) has norm > maxnorm: nmin = min(ns) if nmin > maxnorm: return # extract smallest prime and its index: i = ns.index(nmin) P = Ps[i] # pop the next prime off that sub-iterator, detecting if it has finished: try: Ps[i] = next(PPs[i]) ns[i] = Ps[i].norm() except StopIteration: # prevent i'th sub-iterator from being used again ns[i] = Infinity yield P
def transform_ballots(nodeSet): """ Transforms ballots in node to Sets :param nodeSet: Current node ([ballot_set], upper_bound) :return: List of ballots as Set """ L = [] for nb_voters, ballot in nodeSet: if isinstance(ballot[-1], int): L.append(Set(ballot)) else: L.append(Set(ballot[:-1])) return L
def NAC_types2motion_type(t): r""" Return the motion type for given types of NAC-colorings. """ if Set(t)==Set(['L','R','O']): return 'g' if Set(t)==Set(['L','R']): return 'a' if Set(t)==Set(['O']): return 'p' if Set(t)==Set(['R','O']): return 'e' if Set(t)==Set(['L','O']): return 'o'
def __init__(self, G, coloring, name=None, check=True): from flexible_rigid_graph import FlexRiGraph if type(G) == FlexRiGraph or 'FlexRiGraph' in str( type(G)) or isinstance(G, FlexRiGraph): self._graph = G else: raise exceptions.TypeError('The graph G must be FlexRiGraph.') if type(coloring) in [list, Set] and len(coloring) == 2: self._red_edges = Set([Set(e) for e in coloring[0]]) self._blue_edges = Set([Set(e) for e in coloring[1]]) elif type(coloring) == dict: self._red_edges = Set( [Set(e) for e in coloring if coloring[e] == 'red']) self._blue_edges = Set( [Set(e) for e in coloring if coloring[e] == 'blue']) elif type(coloring) == NACcoloring or isinstance( coloring, NACcoloring) or 'NACcoloring' in str(type(coloring)): self._red_edges = copy(coloring._red_edges) self._blue_edges = copy(coloring._blue_edges) else: raise exceptions.TypeError( 'The coloring must be a dict, list consisting of two lists or an instance of NACcoloring.' ) self._check_edges() self._name = name if check and not self.is_NAC_coloring(): raise exceptions.ValueError('The coloring is not a NAC-coloring.')
def read_strict_pref(count, pref): """ Reads the line and returns the preferences :param count: number of voters having this preference :param pref: ranking of candidates for these voters :type count: int :type pref: str :return: preference formated to fit to our needs :rtype: list """ # temp is the preference for a set of people : # the number of people having this preference, a strict order of preferences # If indifferent between candidates, a Set (from sagemath) of them is added to the list of preferences if '{' in pref: approved = list(map(int, pref[:pref.index("{")].rstrip(",").split(","))) disapproved = list( map(int, pref[pref.index("{") + 1:pref.index("}")].split(","))) else: approved = list(map(int, pref.split(","))) disapproved = [] res = [] for i in range(len(approved)): temp = [ count, approved[:i + 1] + [Set(approved[i + 1:] + disapproved)] ] res.append(temp) return res
def all_symbols(sign, rank, D): symbols = list() # print D fac = Integer(D).factor() symbols = list() for p, v in fac: psymbols = list() parts = Partitions(v) exp_mult = list() for vs in parts: exponents = Set(list(vs)) if len(vs) <= rank: mult = dict() for vv in exponents: mult[vv] = list(vs).count(vv) exp_mult.append(mult) Dp = D // (p**v) for vs in exp_mult: # print "partition:", vs ell = list() if p == 2: for vv, mult in vs.iteritems(): ll = list() for t in [0, 1]: # even(0) or odd(1) type for det in [1, 3, 5, 7]: # the possible determinants if mult % 2 == 0 and t == 0: ll.append([vv, mult, det, 0, 0]) if mult == 1: odds = [det] elif mult == 2: if det in [1, 7]: odds = [0, 2, 6] else: odds = [2, 4, 6] else: odds = [ o for o in range(8) if o % 2 == mult % 2 ] for oddity in odds: if t == 1: ll.append([vv, mult, det, 1, oddity]) ell.append(ll) else: for vv, mult in vs.iteritems(): ll = [[vv, mult, 1], [vv, mult, -1]] ell.append(ll) l = list(itertools.product(*ell)) l = map(lambda x: {p: list(x)}, l) psymbols = psymbols + l if len(symbols) == 0: symbols = psymbols else: symbols_new = list() for sym in symbols: for psym in psymbols: newsym = deepcopy(sym) newsym.update(psym) symbols_new.append(newsym) symbols = symbols_new return symbols
def good_flats(F): S = L[F] if H in S: U = S.difference(Set([H])) return (U != 0) and (not U in L.values()) else: return True
def __init__(self, arg, ambient_dim=None, check=False): try: polyhedra = list(Set(arg)) except TypeError: polyhedra = [arg] if not polyhedra: if ambient_dim is None: raise ValueError('need to specify ambient dimension') self.ambient_dim = ambient_dim self.polyhedra = [] self.cones = [] return self.ambient_dim = polyhedra[0].ambient_dim( ) if ambient_dim is None else ambient_dim polyhedra = [P for P in polyhedra if not P.is_empty()] self.polyhedra = polyhedra self.cones = [conify_polyhedron(P) for P in polyhedra] if any(P.ambient_dim() != self.ambient_dim for P in polyhedra): raise ValueError('inconsistent ambient dimensions') if check and any(not (P & Q).is_empty() for P, Q in itertools.combinations(polyhedra, 2)): raise ValueError('polyhedra are not disjoint')
def _lof_from_affine_matroid(A): from sage.all import Poset, Set from functools import reduce A_coned = A.cone() hyps = list(map(lambda H: H.coefficients(), A_coned.hyperplanes())) extra = [0, 1] + [0] * (A.dimension()) i = hyps.index(extra) P, L, H = _lof_from_matroid(A_coned) assert (H[i + 1]).coefficients() == extra new_elts = list(filter(lambda x: not P.le(i + 1, x), P)) new_elts = reduce(lambda x, y: x + y, [ list(filter(lambda x: P.rank(x) == r, new_elts)) for r in range(2, P.rank() + 1) ], [0] + [j for j in range(1, len(A_coned) + 1) if j != i + 1]) new_names = {x: new_elts.index(x) for x in new_elts} adj_set = lambda S: Set([new_names[x] for x in S if x in new_elts]) P_new = Poset(P.subposet(new_elts), element_labels=new_names) L_new = {new_names[x]: adj_set(L[x]) for x in new_elts} def inv_H(h): cut = lambda x: x.coefficients()[1:] pair = list(filter(lambda x: cut(x[1]) == h, H.items())) return pair[0][0] H_new = {new_names[inv_H(h.coefficients())]: h for h in A} return [P_new, L_new, H_new]
def _lof_from_matroid(A=None, matroid=None): from sage.all import Set, Matrix, Matroid, Poset from functools import reduce if A != None: rows = list(map(lambda H: H.coefficients()[1:], A.hyperplanes())) mat = Matrix(A.base_ring(), rows).transpose() M = Matroid(mat) n = len(A) lbl_map = lambda S: S else: M = matroid.simplify() n = len(M.groundset()) grd_list = list(M.groundset()) l_map = {x: grd_list.index(x) for x in grd_list} lbl_map = lambda S: frozenset([l_map[x] for x in S]) L = M.lattice_of_flats() rank_r = lambda L, r: list( map(lbl_map, filter(lambda x: L.rank(x) == r, L._elements))) rank_1 = [frozenset([k]) for k in range(n)] ranks = reduce(lambda x, y: x + y, [rank_r(L, r) for r in range(2, L.rank() + 1)], [L.bottom()] + rank_1) P = Poset(L, element_labels={x: ranks.index(lbl_map(x)) for x in L._elements}) adj_set = lambda S: Set([x + 1 for x in S]) label_dict = {i: adj_set(ranks[i]) for i in range(len(L))} if A != None: hyp_dict = {i: A[list(ranks[i])[0]] for i in range(1, n + 1)} else: hyp_dict = None return [P, label_dict, hyp_dict]
def is_coherent(ballot, axes): """ Determines if a ballot is coherent with a given axis :param ballot: a ballot for candidates :param axis: axis of preference :return: True if the ballot is coherent with the axis """ return any(ballot == Set(axes[i:i + len(ballot)]) for i in range(len(axes) - len(ballot) + 1))
def isomorphic_NAC_coloring(self, sigma, onlySets=False): r""" Return the NAC-coloring under a morphism ``sigma``. """ if onlySets: return [ Set([Set([sigma(e[0]), sigma(e[1])]) for e in self._red_edges]), Set([ Set([sigma(e[0]), sigma(e[1])]) for e in self._blue_edges ]) ] else: return NACcoloring( self._graph, [[[sigma(e[0]), sigma(e[1])] for e in edges] for edges in [self._red_edges, self._blue_edges]])
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 _grid2motion(self, NAC_coloring, data, check): self._par_type = 'symbolic' alpha = var('alpha') self._field = parent(alpha) self._parameter = alpha self._active_NACs = [NAC_coloring] zigzag = data['zigzag'] grid_coor = NAC_coloring.grid_coordinates(ordered_red=data['red'], ordered_blue=data['blue']) self._same_lengths = [] for i, edges in enumerate([NAC_coloring.blue_edges(), NAC_coloring.red_edges()]): partition = [[list(edges[0])]] for u, v in edges[1:]: appended = False for part in partition: u2, v2 = part[0] if Set([grid_coor[u][i], grid_coor[v][i]]) == Set([grid_coor[u2][i], grid_coor[v2][i]]): part.append([u, v]) appended = True break if not appended: partition.append([[u, v]]) self._same_lengths += partition if check and len(Set(grid_coor.values())) != self._graph.num_verts(): raise exceptions.ValueError('The NAC-coloring does not yield a proper flexible labeling.') if zigzag: if type(zigzag) == list and len(zigzag) == 2: a = [vector(c) for c in zigzag[0]] b = [vector(c) for c in zigzag[1]] else: m = max([k for _, k in grid_coor.values()]) n = max([k for k, _ in grid_coor.values()]) a = [vector([0.3*((-1)**i-1)+0.3*sin(i), i]) for i in range(0,m+1)] b = [vector([j, 0.3*((-1)**j-1)+0.3*sin(j)]) for j in range(0,n+1)] else: m = max([k for _, k in grid_coor.values()]) n = max([k for k, _ in grid_coor.values()]) a = [vector([0, i]) for i in range(0,m+1)] b = [vector([j, 0]) for j in range(0,n+1)] rotation = matrix([[cos(alpha), sin(alpha)], [-sin(alpha), cos(alpha)]]) positions = {} for v in self._graph.vertices(): positions[v] = rotation * a[grid_coor[v][1]] + b[grid_coor[v][0]] self._parametrization = positions