def test_factoradics(): for i in range(100): assert from_factoradic(to_factoradic(i)) == i assert tuple(map(to_factoradic, range(10))) == ( (0,), (1, 0,), (1, 0, 0), (1, 1, 0), (2, 0, 0), (2, 1, 0), (1, 0, 0, 0), (1, 0, 1, 0), (1, 1, 0, 0), (1, 1, 1, 0) )
def index(self, perm): '''Get the index number of permutation `perm` in this space.''' if not isinstance(perm, collections.abc.Iterable): raise ValueError perm = sequence_tools.ensure_iterable_is_immutable_sequence(perm) perm_set = set(perm) if not isinstance(perm, UnrecurrentedPerm) \ else set(perm._perm_sequence) if not (perm_set <= set(self.sequence)): raise ValueError if sequence_tools.get_length(perm) != self.n_elements: raise ValueError if not isinstance(perm, self.perm_type): perm = self.perm_type(perm, self) if self.sequence != perm.nominal_perm_space.sequence: # (This also covers `self.rapplied != perm.rapplied`) raise ValueError if self.domain != perm.domain: # (This also covers `self.dapplied != perm.dapplied`) raise ValueError if self.is_degreed and (perm.degree not in self.degrees): raise ValueError # At this point we know the permutation contains the correct items, and # has the correct degree. if perm.is_dapplied: return self.undapplied.index(perm.undapplied) ####################################################################### elif self.is_degreed: if perm.is_rapplied: return self.unrapplied.index(perm.unrapplied) wip_perm_number = 0 wip_perm_sequence_dict = dict(self.fixed_map) unused_values = list(self.free_values) for i, value in enumerate(perm._perm_sequence): if i in self.fixed_indices: continue unused_values.remove(value) lower_values = [j for j in unused_values if j < value] for lower_value in lower_values: temp_fixed_map = dict(wip_perm_sequence_dict) temp_fixed_map[i] = lower_value wip_perm_number += PermSpace( self.sequence_length, degrees=self.degrees, fixed_map=temp_fixed_map).length wip_perm_sequence_dict[self.domain[i]] = value perm_number = wip_perm_number ####################################################################### elif self.is_recurrent: assert not self.is_degreed and not self.is_dapplied wip_perm_number = 0 unused_values = list(self.sequence) reserved_values = list(self.fixed_map.values()) perm_sequence_list = list(perm._perm_sequence) shit_set = set() for i, value in enumerate(perm._perm_sequence): if i in self.fixed_map: if self.fixed_map[i] == value: unused_values.remove(value) reserved_values.remove(value) continue else: raise ValueError lower_values = [ thing for thing in nifty_collections.OrderedSet(unused_values) if (thing not in reserved_values or unused_values.count( thing) > reserved_values.count(thing)) and unused_values.index(thing) < unused_values.index(value) and thing not in shit_set ] unused_values.remove(value) for lower_value in lower_values: temp_fixed_map = dict( enumerate(perm_sequence_list[:i] + [lower_value])) temp_fixed_map.update(self.fixed_map) candidate_sub_perm_space = \ PermSpace._create_with_cut_prefix( self.sequence, n_elements=self.n_elements, fixed_map=temp_fixed_map, is_combination=self.is_combination, shit_set=shit_set, perm_type=self.perm_type ) wip_perm_number += candidate_sub_perm_space.length if self.is_combination: shit_set.add(lower_value) perm_number = wip_perm_number ####################################################################### elif self.is_fixed: assert not self.is_degreed and not self.is_recurrent free_values_perm_sequence = [] for i, perm_item in zip(self.domain, perm._perm_sequence): if i in self.fixed_map: if self.fixed_map[i] != perm_item: raise ValueError else: free_values_perm_sequence.append(perm_item) # At this point we know all the items that should be fixed are # fixed. perm_number = self._free_values_unsliced_perm_space.index( free_values_perm_sequence) ####################################################################### elif self.is_combination: if perm.is_rapplied: return self.unrapplied.index(perm.unrapplied) assert not self.is_rapplied and not self.is_recurrent and \ not self.is_dapplied and not self.is_fixed and \ not self.is_degreed if not cute_iter_tools.is_sorted(perm._perm_sequence): raise ValueError processed_perm_sequence = tuple( self.sequence_length - 1 - item for item in perm._perm_sequence[::-1]) perm_number = self.unsliced.length - 1 - sum( (math_tools.binomial(item, i) for i, item in enumerate(processed_perm_sequence, start=1)), 0) ####################################################################### else: factoradic_number = [] unused_values = list(self.sequence) for i, value in enumerate(perm._perm_sequence): index_of_current_number = unused_values.index(value) factoradic_number.append(index_of_current_number) unused_values.remove(value) perm_number = math_tools.from_factoradic( factoradic_number + [0] * self.n_unused_elements) // math.factorial( self.n_unused_elements) ####################################################################### if perm_number not in self.canonical_slice: raise ValueError return perm_number - self.canonical_slice.start
def index(self, perm): '''Get the index number of permutation `perm` in this space.''' if not isinstance(perm, collections.Iterable): raise ValueError try: perm = sequence_tools.ensure_iterable_is_immutable_sequence( perm, allow_unordered=False ) except sequence_tools.UnorderedIterableException: raise ValueError('An unordered iterable is never contained in a ' '`PermSpace`. Try an ordered one.') perm_set = set(perm) if not isinstance(perm, UnrecurrentedPerm) \ else set(perm._perm_sequence) if not (perm_set <= set(self.sequence)): raise ValueError if sequence_tools.get_length(perm) != self.n_elements: raise ValueError if not isinstance(perm, self.perm_type): perm = self.perm_type(perm, self) if self.sequence != perm.nominal_perm_space.sequence: # (This also covers `self.rapplied != perm.rapplied`) raise ValueError if self.domain != perm.domain: # (This also covers `self.dapplied != perm.dapplied`) raise ValueError if self.is_degreed and (perm.degree not in self.degrees): raise ValueError # At this point we know the permutation contains the correct items, and # has the correct degree. if perm.is_dapplied: return self.undapplied.index(perm.undapplied) ####################################################################### elif self.is_degreed: if perm.is_rapplied: return self.unrapplied.index(perm.unrapplied) wip_perm_number = 0 wip_perm_sequence_dict = dict(self.fixed_map) unused_values = list(self.free_values) for i, value in enumerate(perm._perm_sequence): if i in self.fixed_indices: continue unused_values.remove(value) lower_values = [j for j in unused_values if j < value] for lower_value in lower_values: temp_fixed_map = dict(wip_perm_sequence_dict) temp_fixed_map[i] = lower_value wip_perm_number += PermSpace( self.sequence_length, degrees=self.degrees, fixed_map=temp_fixed_map ).length wip_perm_sequence_dict[self.domain[i]] = value perm_number = wip_perm_number ####################################################################### elif self.is_recurrent: assert not self.is_degreed and not self.is_dapplied wip_perm_number = 0 unused_values = list(self.sequence) reserved_values = list(self.fixed_map.values()) perm_sequence_list = list(perm._perm_sequence) shit_set = set() for i, value in enumerate(perm._perm_sequence): if i in self.fixed_map: if self.fixed_map[i] == value: unused_values.remove(value) reserved_values.remove(value) continue else: raise ValueError lower_values = [ thing for thing in nifty_collections.OrderedSet(unused_values) if (thing not in reserved_values or unused_values.count(thing) > reserved_values.count(thing)) and unused_values.index(thing) < unused_values.index(value) and thing not in shit_set ] unused_values.remove(value) for lower_value in lower_values: temp_fixed_map = dict( enumerate(perm_sequence_list[:i] + [lower_value]) ) temp_fixed_map.update(self.fixed_map) candidate_sub_perm_space = \ PermSpace._create_with_cut_prefix( self.sequence, n_elements=self.n_elements, fixed_map=temp_fixed_map, is_combination=self.is_combination, shit_set=shit_set, perm_type=self.perm_type ) wip_perm_number += candidate_sub_perm_space.length if self.is_combination: shit_set.add(lower_value) perm_number = wip_perm_number ####################################################################### elif self.is_fixed: assert not self.is_degreed and not self.is_recurrent free_values_perm_sequence = [] for i, perm_item in zip(self.domain, perm._perm_sequence): if i in self.fixed_map: if self.fixed_map[i] != perm_item: raise ValueError else: free_values_perm_sequence.append(perm_item) # At this point we know all the items that should be fixed are # fixed. perm_number = self._free_values_unsliced_perm_space.index( free_values_perm_sequence ) ####################################################################### elif self.is_combination: if perm.is_rapplied: return self.unrapplied.index(perm.unrapplied) assert not self.is_rapplied and not self.is_recurrent and \ not self.is_dapplied and not self.is_fixed and \ not self.is_degreed if not cute_iter_tools.is_sorted(perm._perm_sequence): raise ValueError processed_perm_sequence = tuple( self.sequence_length - 1 - item for item in perm._perm_sequence[::-1] ) perm_number = self.unsliced.length - 1 - sum( (math_tools.binomial(item, i) for i, item in enumerate(processed_perm_sequence, start=1)), 0 ) ####################################################################### else: factoradic_number = [] unused_values = list(self.sequence) for i, value in enumerate(perm._perm_sequence): index_of_current_number = unused_values.index(value) factoradic_number.append(index_of_current_number) unused_values.remove(value) perm_number = math_tools.from_factoradic( factoradic_number + [0] * self.n_unused_elements ) // math.factorial(self.n_unused_elements) ####################################################################### if perm_number not in self.canonical_slice: raise ValueError return perm_number - self.canonical_slice.start