Source code for sator.setbase

#!/usr/bin/env python
from itertools import count, izip, combinations

import sator.utils as utils
from sator.const import Z_PARTNERS

from sator.setrowbase import SetRowBase, PCBase


[docs]class SetBase(SetRowBase): """Base class for PCSet and PSet""" _canon_t = True _canon_i = True _canon_m = False def __sub__(self, other): """Remove all instances of a given pc from a pcset""" rm_pcs = other if isinstance(other, (int, long)): rm_pcs = [other] if isinstance(other, set): rm_pcs = [int(num) for num in other] results = self.pitches[:] for pc in rm_pcs: while pc in results: results.remove(pc) return self.copy(results)
[docs] def insert(self, place, pitch): """ Given arguments (place, pitch) insert the pitch at the place position. Take care to inspect the object's pitches attribute rather than it's __repr__, which uses the ppc attribute and may truncate duplicates. If the position is too great, the pitch will be appended at the end. """ try: pitches = self.pitches[:place] pitches.append(pitch) pitches.extend(self.pitches[place:]) self[:] = pitches except IndexError: self[:].append(pitch)
[docs] def clear(self): """Remove all pitches/pitch classes from the object.""" self[:] = []
[docs] def canon(self, t, i, m): """ Takes arguments in the form of (T, I, M) where each is a boolean. These arguments determine which TTO's are canonical. These TTO's are used to determine an object's set-class. (The default canonical operators are T and I, hence the common name Tn/TnI type). Ex: a.canon(True, False, False) a.prime would now give the Tn-type, and ignore inversion as an operation for determining set-class membership. """ self._canon_t = True if t else False self._canon_i = True if i else False self._canon_m = True if m else False
@property
[docs] def get_canon(self): """ Returns a three tuple showing which TTO's are canonical for the given object. These are in the order (T, I, M). Refer to canon() for details on how these settings are used. """ return (self._canon_t, self._canon_i, self._canon_m)
@staticmethod
[docs] def fromint(integer, modulus=12): """ Static method that returns a PCSet object with pc's generated from their integer representation. Ex: 0 = [], 1 = [0], 2 = [1], 3 = [0, 1], 4 = [2], 5 = [0, 2] PCSet.fromint(5) returns PCSet([0, 2]) """ from sator.pcset import PCSet new_set = PCSet(mod=modulus) new_set.pitches = utils.fromint(integer) return new_set
@property
[docs] def setint(self): """ Returns the integer representation for the unique PC's in a given object """ return utils.setint(self._unique_pcs)
@property
[docs] def pcint(self): """ Returns the integer representation of a given object in prime form. """ return utils.setint(self.prime)
[docs] def each_set(self): """ Yields every possible set in the modulus of the given object. """ return self.each_set_in_mod(self._mod)
[docs] def each_prime(self): """ Yields each unique set-class in the modulus of the given object. """ return self.each_prime_in_mod(self._mod)
[docs] def each_card(self): """ Yields every set with the same cardinality as the given object, taking into account the object's modulus. """ return self.each_card_in_mod(self.cardinality, self._mod)
@classmethod def each_set_in_mod(cls, mod): return(cls(utils.fromint(integer), mod=mod) for integer in xrange(0, 2 ** mod)) @classmethod def each_prime_in_mod(cls, mod): return(each for each in cls.each_set_in_mod(mod) if each.prime == each) @classmethod
[docs] def each_card_in_mod(cls, card, mod): """ Same as the instance method but takes two args for cardinality and modulus respectively """ return (cls(each, mod=mod) for each in combinations(cls.each_n_in_mod(mod), card))
@classmethod
[docs] def each_prime_in_card_mod(cls, card, mod): """ Yields every unique prime form with a given cardinality in the given modulus """ for each in combinations(cls.each_n_in_mod(mod), card): new = cls(each, mod=mod) if new.prime == new: yield new
@property
[docs] def cardinality(self): """Returns the cardinality of the given object.""" return len(self._pc_set) # Helper functions for prime()
def _t_rotations(self): """ Same as the public method, but enforces that the returned objects are PCSets for use with the prime method. """ from sator.pcset import PCSet return (PCSet(self.copy(rot)) for rot in [self._transpose(n) \ for n in self.each_n()]) def _i_rotations(self): """ Same as the public method, but enforces that the returned objects are PCSets for use with the prime method. """ from sator.pcset import PCSet return (PCSet(self.copy(rot)) for rot in [self._invert(n) \ for n in self.each_n()]) def _m_rotations(self): """ Same as the public method, but enforces that the returned objects are PCSets for use with the prime method. """ from sator.pcset import PCSet return (PCSet(self.copy(rot)) for rot in [self._transpose_multiply(n) \ for n in self.each_n()]) def _mi_rotations(self): """ Same as the public method, but enforces that the returned objects are PCSets for use with the prime method. """ from sator.pcset import PCSet return (PCSet(self.copy(rot)) for rot in [self._transpose_multiply(n, self._default_m * -1) \ for n in self.each_n()]) @property def _rotation_ints(self): """ Returns a nested list with the interger representation for each object returned from _rotations() """ return [[utils.setint(pcs) for pcs in rotation] \ for rotation in self._rotations()] def _rotations(self): """ Returns a nested list of PCSet objects for each canonical TTO for a given object. """ def setify(pitches): pcs = [pitch % self._mod for pitch in pitches] result = list(set(pitches)) result.sort() return result result = [] if self._canon_t: result.append(self._t_rotations()) if self._canon_i: result.append(self._i_rotations()) if self._canon_m: result.append(self._m_rotations()) if self._canon_i and self._canon_m: result.append(self._mi_rotations()) else: result.append([setify(self.pcs)]) if self._canon_i: result.append([setify(self._invert())]) if self._canon_m: result.append([setify(self._transpose_multiply(0))]) return result @property
[docs] def prime_operation(self): """ A property that returns (n, m) to perform on the given object via TnMm in order to obtain its prime form. """ low_vals =[min(izip(operation, count())) \ for operation in self._rotation_ints] min_val = min(izip(low_vals, count())) n = min_val[0][1] oper = min_val[1] if oper == 0: m = 1 elif oper == 1: m = self._mod - 1 elif oper == 2: m = self._default_m elif oper == 3: m = self._mod - self._default_m return (n, m)
@property
[docs] def prime(self): """ Return a PCSet that represents the given object in prime form, taking into account its canonical TTO's (set these with .canon(T, I, M)). """ from sator.pcset import PCSet n, m = self.prime_operation return PCSet(self.copy(utils.transpose_multiply(self.pitches, n, m)))
@property
[docs] def mpartner(self): """ Return a PCSet for the M-partner of the given object. """ from sator.pcset import PCSet return PCSet(self._transpose_multiply().prime)
@staticmethod
[docs] def forte_name(fname): """ A static method that returns a PCSet object with the fort-name provided as a string argument. Returns an empty PCSet if the argument is not a string with a valid Forte name. """ from sator.pcset import PCSet fset = utils.from_forte(fname) new_set = PCSet() if fset: new_set.pitches = fset return new_set
@property
[docs] def forte(self): """ Returns the Forte name for the given object. """ return utils.forte_name(self.pcint)
@property
[docs] def literal_compliment(self): """Returns a PCSet of the literal compliment of the given object.""" from sator.pcset import PCSet return PCSet(self.copy([n for n in self.each_n() if n not in self.pcs]))
@property
[docs] def abstract_compliment(self): """Returns a PCSet of the abstract compliment of the given object.""" from sator.pcset import PCSet return PCSet(self.literal_compliment.prime)
@property
[docs] def icv(self): """Returns the interval class vector of the given object.""" return utils.icv(self._unique_pcs, self._mod)
@property
[docs] def zpartner(self): """ Property that returns the Z-partner of the given object if it exists, otherwise returns None. """ if self._mod == 12: zint = Z_PARTNERS.get(self.pcint, None) if zint: return self.copy(utils.fromint(zint)) else: return for each in self.each_card(): if each.icv == self.icv: if each.prime._unique_pcs != self.prime._unique_pcs: return self.copy(p)
@property
[docs] def invariance_vector(self): """ A property that returns the list of (n, m) pairs that produce an invariant set via TnMm """ return [(n, m) for n, m in self.each_tto() if self._transpose_multiply(n, m) == self]
@property
[docs] def ds(self): """ Degrees of symmetry (number of Tn/TnI operations for which this set is invariant) """ total = 0 for m in (1, -1): for n in self.each_n(): if self._transpose_multiply(n, m) == self: total += 1 return total
[docs] def m_vector(self, m): """ Find David Lewin's M-vector. (Also described in Composition with Pitch Classes - Robert Morris) Finds the number of each set-class with cardinality m which are subsets of a given pitch class. The ICV is equivalent to the m-vector of a pitch class when m is 2. """ if m > self._mod: return from sator.pcset import PCSet pc_set = self._pc_set vectors = {} for index, each in enumerate(self.__class__.each_card_in_mod(m, self._mod)): e_prime = each.pcint old_value = vectors.get(e_prime, 0) to_add = 1 if pc_set.issuperset(each._pc_set) else 0 vectors[e_prime] = old_value + to_add return sorted([(PCSet.fromint(pcint), total) \ for pcint, total in vectors.items()], key = lambda x: x[0].pcint)
[docs] def supersets(self, limit=0): """ Yields the supersets of the given object. Takes an optional argument, which limits the supersets to those with a cardinality <= the limit. With no argument, returns all supersets. """ for sup in utils.supersets(self.ppc, self._mod, limit): yield self.copy(sup)
[docs] def superprimes(self, limit=0): """ Yields the supersets of the given object which have a unique set-class. Takes an optional limit argument with the same behavior as supersets() """ from sator.pcset import PCSet for sup in self.supersets(limit): yield PCSet(self.copy(utils.fromint(sup.pcint)))
[docs] def subsets(self, limit=0): """ Yields the subsets of the given object. Takes an optional argument, which limits the subsets to those with a cardinality >= the limit. With no argument, returns all subsets. """ for sub in utils.subsets(self.ppc, limit): yield self.copy(sub)
[docs] def subprimes(self, limit=0): """ Yields the subsets of the given object which have a unique set-class. Takes an optional limit argument with the same behavior as subsets(). """ from sator.pcset import PCSet for sub in self.subsets(limit): yield PCSet(self.copy(utils.fromint(sub.pcint)))
[docs] class OnlySetableMethod(Exception): """ Exception to raise if the argument used can not be made into a set """ pass
[docs] def args_are_sets(f, *args, **kwargs): """ Decorator to help with set methods. Ensures that args are sets""" def _(*args, **kwargs): from sator.pcset import PCSet from sator.pset import PSet ints = set() for arg in args[1:]: if isinstance(arg, (PCSet, PSet)): arg = arg._pc_set continue if isinstance(arg, (list, tuple)): arg = set(arg) continue if isinstance(arg, set): continue err_msg = 'Only lists, tuples, sets, PSet, and PCSet objects can be used as arguments for this method' raise args[0].OnlySetableMethod(err_msg) return f(*args, **kwargs) _.__doc__ = f.__doc__ _.__name__ = f.__name__ return _
@args_are_sets
[docs] def union(self, other): """ Return an instance that represents the union of the current PSet or PCSet and another as the first and only positional argument. """ return self.copy(self._pc_set.union(other))
@args_are_sets
[docs] def intersection(self, other): """ Return an instance that represents the intersection of the current PSet or PCSet and another as the first and only positional argument. """ return self.copy(self._pc_set.intersection(other))
@args_are_sets
[docs] def difference(self, other): """ Return an instance that represents the difference of the current PSet or PCSet and another as the first and only positional argument. """ return self.copy(self._pc_set.difference(other))
@args_are_sets
[docs] def symmetric_difference(self, other): """ Return an instance that represents the symmetric_difference of the current PSet or PCSet and another as the first and only positional argument. """ return self.copy(self._pc_set.symmetric_difference(other))
@args_are_sets
[docs] def issubset(self, other): """ Return True if the current PSet or PCSet is a subset of another object taken as the first and only positional argument, otherwise False. """ return self._pc_set.issubset(other)
@args_are_sets
[docs] def issuperset(self, other): """ Return True if the current PSet or PCSet is a superset of another object taken as the first and only positional argument, otherwise False """ return self._pc_set.issuperset(other)
@args_are_sets
[docs] def isdisjoint(self, other): """ Return True if the current PSet or PCSet is disjoint with another object taken as the first and only positional argument, otherwise False """ return self._pc_set.isdisjoint(other)

Project Versions