def test(): assert binomial(7, 3) == 35 assert binomial(0, 0) == 1 assert binomial(1, 0) == 1 assert binomial(0, 1) == 0 assert binomial( 1543, 634 ) == 127103521979248139661884595050302692072114625333816461647571438364482801578062268185939019831927710543644891679108093639691467979466411177318250931548804667267192030646116875003881007119966764992383016174407823444352165249728639847571592229832113238415348489512831060701471487834812550521403788703534654431344329462541634971732197170414906629071055802381322184009159362499960475076746698583466181504060523973736761406471112019069930703149760846502951040
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 __getitem__(self, i): if isinstance(i, (slice, sequence_tools.CanonicalSlice)): canonical_slice = sequence_tools.CanonicalSlice( i, self.length, offset=self.canonical_slice.start) return PermSpace(self.sequence, domain=self.domain, n_elements=self.n_elements, fixed_map=self.fixed_map, degrees=self.degrees, is_combination=self.is_combination, slice_=canonical_slice, perm_type=self.perm_type) assert isinstance(i, numbers.Integral) if i <= -1: i += self.length if not (0 <= i < self.length): raise IndexError elif self.is_sliced: return self.unsliced[i + self.canonical_slice.start] elif self.is_dapplied: return self.perm_type(self.undapplied[i], perm_space=self) ####################################################################### elif self.is_degreed: if self.is_rapplied: assert not self.is_recurrent and \ not self.is_partial and not self.is_combination and \ not self.is_dapplied and not self.is_sliced return self.perm_type(map(self.sequence.__getitem__, self.unrapplied[i]), perm_space=self) assert not self.is_rapplied and not self.is_recurrent and \ not self.is_partial and not self.is_combination and \ not self.is_dapplied and not self.is_sliced # If that wasn't an example of asserting one's dominance, I don't # know what is. available_values = list(self.free_values) wip_perm_sequence_dict = dict(self.fixed_map) wip_n_cycles_in_fixed_items = \ self._n_cycles_in_fixed_items_of_just_fixed wip_i = i for j in self.sequence: if j in wip_perm_sequence_dict: continue for unused_value in available_values: candidate_perm_sequence_dict = dict(wip_perm_sequence_dict) candidate_perm_sequence_dict[j] = unused_value ### Checking whether we closed a cycle: ################### # # if j == unused_value: closed_cycle = True else: current = j while True: current = candidate_perm_sequence_dict[current] if current == j: closed_cycle = True break elif current not in candidate_perm_sequence_dict: closed_cycle = False break # # ### Finished checking whether we closed a cycle. ########## candidate_n_cycles_in_fixed_items = \ wip_n_cycles_in_fixed_items + closed_cycle candidate_fixed_perm_space_length = sum( math_tools.abs_stirling( self.sequence_length - len(candidate_perm_sequence_dict), self.sequence_length - degree - candidate_n_cycles_in_fixed_items) for degree in self.degrees) if wip_i < candidate_fixed_perm_space_length: available_values.remove(unused_value) wip_perm_sequence_dict[j] = unused_value wip_n_cycles_in_fixed_items = \ candidate_n_cycles_in_fixed_items break wip_i -= candidate_fixed_perm_space_length else: raise RuntimeError assert wip_i == 0 return self.perm_type( (wip_perm_sequence_dict[k] for k in self.domain), self) ####################################################################### elif self.is_recurrent: assert not self.is_dapplied and not self.is_degreed and \ not self.is_sliced available_values = list(self.sequence) reserved_values = nifty_collections.Bag(self.fixed_map.values()) wip_perm_sequence_dict = dict(self.fixed_map) wip_i = i shit_set = set() for j in range(self.n_elements): if j in self.fixed_map: available_values.remove(self.fixed_map[j]) reserved_values[self.fixed_map[j]] -= 1 continue unused_values = [ item for item in nifty_collections.OrderedBag(available_values) - reserved_values if item not in shit_set ] for unused_value in unused_values: wip_perm_sequence_dict[j] = unused_value candidate_sub_perm_space = \ PermSpace._create_with_cut_prefix( self.sequence, n_elements=self.n_elements, fixed_map=wip_perm_sequence_dict, is_combination=self.is_combination, shit_set=shit_set, perm_type=self.perm_type ) if wip_i < candidate_sub_perm_space.length: available_values.remove(unused_value) break else: wip_i -= candidate_sub_perm_space.length if self.is_combination: shit_set.add(wip_perm_sequence_dict[j]) del wip_perm_sequence_dict[j] else: raise RuntimeError assert wip_i == 0 return self.perm_type( dict_tools.get_tuple(wip_perm_sequence_dict, self.domain), self) ####################################################################### elif self.is_fixed: free_values_perm = self._free_values_unsliced_perm_space[i] free_values_perm_iterator = iter(free_values_perm) return self.perm_type( tuple((self._undapplied_fixed_map[m] if ( m in self.fixed_indices ) else next(free_values_perm_iterator)) for m in self.indices), self) ####################################################################### elif self.is_combination: wip_number = self.length - 1 - i wip_perm_sequence = [] for i in range(self.n_elements, 0, -1): for j in range(self.sequence_length, i - 2, -1): candidate = math_tools.binomial(j, i) if candidate <= wip_number: wip_perm_sequence.append(self.sequence[-(j + 1)]) wip_number -= candidate break else: raise RuntimeError result = tuple(wip_perm_sequence) assert len(result) == self.n_elements return self.perm_type(result, self) ####################################################################### else: factoradic_number = math_tools.to_factoradic( i * math.factorial(self.n_unused_elements), n_digits_pad=self.sequence_length) if self.is_partial: factoradic_number = factoradic_number[:-self.n_unused_elements] unused_numbers = list(self.sequence) result = tuple( unused_numbers.pop(factoradic_digit) for factoradic_digit in factoradic_number) assert sequence_tools.get_length(result) == self.n_elements return self.perm_type(result, self)
def test(): assert binomial(7, 3) == 35 assert binomial(0, 0) == 1 assert binomial(1, 0) == 1 assert binomial(0, 1) == 0 assert binomial(1543, 634) == 127103521979248139661884595050302692072114625333816461647571438364482801578062268185939019831927710543644891679108093639691467979466411177318250931548804667267192030646116875003881007119966764992383016174407823444352165249728639847571592229832113238415348489512831060701471487834812550521403788703534654431344329462541634971732197170414906629071055802381322184009159362499960475076746698583466181504060523973736761406471112019069930703149760846502951040
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
def __getitem__(self, i): if isinstance(i, (slice, sequence_tools.CanonicalSlice)): canonical_slice = sequence_tools.CanonicalSlice( i, self.length, offset=self.canonical_slice.start ) return PermSpace( self.sequence, domain=self.domain, n_elements=self.n_elements, fixed_map=self.fixed_map, degrees=self.degrees, is_combination=self.is_combination, slice_=canonical_slice, perm_type=self.perm_type ) assert isinstance(i, numbers.Integral) if i <= -1: i += self.length if not (0 <= i < self.length): raise IndexError elif self.is_sliced: return self.unsliced[i + self.canonical_slice.start] elif self.is_dapplied: return self.perm_type(self.undapplied[i], perm_space=self) ####################################################################### elif self.is_degreed: if self.is_rapplied: assert not self.is_recurrent and \ not self.is_partial and not self.is_combination and \ not self.is_dapplied and not self.is_sliced return self.perm_type(map(self.sequence.__getitem__, self.unrapplied[i]), perm_space=self) assert not self.is_rapplied and not self.is_recurrent and \ not self.is_partial and not self.is_combination and \ not self.is_dapplied and not self.is_sliced # If that wasn't an example of asserting one's dominance, I don't # know what is. available_values = list(self.free_values) wip_perm_sequence_dict = dict(self.fixed_map) wip_n_cycles_in_fixed_items = \ self._n_cycles_in_fixed_items_of_just_fixed wip_i = i for j in self.sequence: if j in wip_perm_sequence_dict: continue for unused_value in available_values: candidate_perm_sequence_dict = dict(wip_perm_sequence_dict) candidate_perm_sequence_dict[j] = unused_value ### Checking whether we closed a cycle: ################### # # if j == unused_value: closed_cycle = True else: current = j while True: current = candidate_perm_sequence_dict[current] if current == j: closed_cycle = True break elif current not in candidate_perm_sequence_dict: closed_cycle = False break # # ### Finished checking whether we closed a cycle. ########## candidate_n_cycles_in_fixed_items = \ wip_n_cycles_in_fixed_items + closed_cycle candidate_fixed_perm_space_length = sum( math_tools.abs_stirling( self.sequence_length - len(candidate_perm_sequence_dict), self.sequence_length - degree - candidate_n_cycles_in_fixed_items ) for degree in self.degrees ) if wip_i < candidate_fixed_perm_space_length: available_values.remove(unused_value) wip_perm_sequence_dict[j] = unused_value wip_n_cycles_in_fixed_items = \ candidate_n_cycles_in_fixed_items break wip_i -= candidate_fixed_perm_space_length else: raise RuntimeError assert wip_i == 0 return self.perm_type((wip_perm_sequence_dict[k] for k in self.domain), self) ####################################################################### elif self.is_recurrent: assert not self.is_dapplied and not self.is_degreed and \ not self.is_sliced available_values = list(self.sequence) reserved_values = nifty_collections.Bag(self.fixed_map.values()) wip_perm_sequence_dict = dict(self.fixed_map) wip_i = i shit_set = set() for j in range(self.n_elements): if j in self.fixed_map: available_values.remove(self.fixed_map[j]) reserved_values[self.fixed_map[j]] -= 1 continue unused_values = [ item for item in nifty_collections.OrderedBag(available_values) - reserved_values if item not in shit_set ] for unused_value in unused_values: wip_perm_sequence_dict[j] = unused_value candidate_sub_perm_space = \ PermSpace._create_with_cut_prefix( self.sequence, n_elements=self.n_elements, fixed_map=wip_perm_sequence_dict, is_combination=self.is_combination, shit_set=shit_set, perm_type=self.perm_type ) if wip_i < candidate_sub_perm_space.length: available_values.remove(unused_value) break else: wip_i -= candidate_sub_perm_space.length if self.is_combination: shit_set.add(wip_perm_sequence_dict[j]) del wip_perm_sequence_dict[j] else: raise RuntimeError assert wip_i == 0 return self.perm_type( dict_tools.get_tuple(wip_perm_sequence_dict, self.domain), self ) ####################################################################### elif self.is_fixed: free_values_perm = self._free_values_unsliced_perm_space[i] free_values_perm_iterator = iter(free_values_perm) return self.perm_type( tuple( (self._undapplied_fixed_map[m] if (m in self.fixed_indices) else next(free_values_perm_iterator)) for m in range(self.sequence_length) ), self ) ####################################################################### elif self.is_combination: wip_number = self.length - 1 - i wip_perm_sequence = [] for i in range(self.n_elements, 0, -1): for j in range(self.sequence_length, i - 2, -1): candidate = math_tools.binomial(j, i) if candidate <= wip_number: wip_perm_sequence.append( self.sequence[-(j+1)] ) wip_number -= candidate break else: raise RuntimeError result = tuple(wip_perm_sequence) assert len(result) == self.n_elements return self.perm_type(result, self) ####################################################################### else: factoradic_number = math_tools.to_factoradic( i * math.factorial( self.n_unused_elements), n_digits_pad=self.sequence_length ) if self.is_partial: factoradic_number = factoradic_number[:-self.n_unused_elements] unused_numbers = list(self.sequence) result = tuple(unused_numbers.pop(factoradic_digit) for factoradic_digit in factoradic_number) assert sequence_tools.get_length(result) == self.n_elements return self.perm_type(result, self)