import numpy as np
[docs]class Configuration:
"""
A :any:`Configuration` describes a specific arrangement of objects in the vector space of possible positions.
Objects are represented by integers, with indistinguishable objects denoted by identical integers.
This class subclasses `numpy.matrix <https://docs.scipy.org/doc/numpy/reference/generated/numpy.matrix.html>`_.
Each configuration in the vector space of positions is represented as a column vector.
Attributes:
count (int): If symmetry-inequivalent configurations have been generated for a `configuration space`,
this records the number of configurations equivalent to this one.
Value at initialisation is ``None``.
lowest_numeric_representation (int): If the numeric representations for the set of equivalent
configurations are calculated, this can be used to store the lowest valued numeric
representation, for use as a simple hash.
Example:
>>> Configuration( [1, 1, 0] )
Configuration([1, 1, 0])
"""
def __init__( self, vector ):
self.count = None
self.lowest_numeric_representation = None
self.vector = np.array( vector )
[docs] def matches( self, test_configuration ):
"""
Test whether this configuration is equal to another configuration.
Args:
test_configuration (:any:`Configuration`): The configuration to compare against.
Returns:
(bool): True | False.
"""
if not isinstance( test_configuration, Configuration ):
raise TypeError
return ( self.vector == test_configuration.vector ).all()
[docs] def is_equivalent_to( self, test_configuration, symmetry_operations ):
"""
Test whether this configuration is equivalent to another configuration
under one or more of a set of symmetry operations.
Args:
test_configuration (Configuration): The configuration to compare against.
symmetry_operations (list(SymmetryOperation): A list of SymmetryOperation objects.
Returns:
(bool): True | False
"""
for symmetry_operation in symmetry_operations:
if ( symmetry_operation.operate_on( self ).matches( test_configuration ) ):
return True
else:
return False
[docs] def is_in_list( self, the_list ):
"""
Test whether this configuration is in a list of configurations.
Args:
list (list(bsym.Comfiguration)): A list of Configuration instances.
Returns:
(bool): True | False
"""
return next( ( True for c in the_list if self.matches( c ) ), False )
[docs] def has_equivalent_in_list( self, the_list, symmetry_operations ):
"""
Test whether this configuration is equivalent by symmetry to one or more
in a list of configurations.
Args:
list (list(bsym.Configuration)): A list of :any:`Configuration` instances.
symmetry_operations (list(bsym.SymmetryOperation)): A list of :any:`SymmetryOperation` objects.
Returns:
(bool): True | False
"""
return next( ( True for c in the_list if self.is_equivalent_to( c, symmetry_operations ) ), False )
[docs] def set_lowest_numeric_representation( self, symmetry_operations ):
"""
Sets `self.lowest_numeric_representation` to the lowest value numeric representation of this configuration when permutated under a set of symmetry operations.
Args:
symmetry_operations (list): A list of :any:`SymmetryOperation` instances.
Returns:
None
"""
self.lowest_numeric_representation = min( [ symmetry_operation.operate_on( self ).as_number for symmetry_operation in symmetry_operations ] )
[docs] def numeric_equivalents( self, symmetry_operations ):
"""
Returns a list of all symmetry equivalent configurations generated by a set of symmetry operations
with each configuration given in numeric representation.
Args:
symmetry_operations (list): A list of :any:`SymmetryOperation` instances.
Returns:
(list(int)): A list of numbers representing each equivalent configuration.
"""
return [ symmetry_operation.operate_on( self ).as_number for symmetry_operation in symmetry_operations ]
#return [ as_number( symmetry_operation.operate_on( self, config=False ) ) for symmetry_operation in symmetry_operations ]
@property
def as_number( self ):
"""
A numeric representation of this configuration.
Examples:
>>> c = Configuration( [ 1, 2, 0 ] )
>>> c.as_number
120
"""
return as_number( self.vector )
[docs] @classmethod
def from_tuple( cls, this_tuple ):
"""
Construct a :any:`Configuration` from a `tuple`,
e.g.::
Configuration.from_tuple( ( 1, 1, 0 ) )
Args:
this_tuple (tuple): The tuple used to construct this :any:`Configuration`.
Returns:
(:any:`Configuration`): The new :any:`Configuration`.
"""
return( cls( this_tuple ) )
[docs] def tolist( self ):
"""
Returns the configuration data as a list.
Args:
None
Returns:
(List)
"""
return list( self.vector )
[docs] def pprint( self ):
print( ' '.join( [ str(e) for e in self.tolist() ] ) )
[docs] def position( self, label ):
"""
Returns the vector indices where elements are equal to `label`.
Args:
label (int): The label used to select the vector positions.
Returns:
(list): A list of all positions that match `label`.
"""
return [ i for i,x in enumerate( self.tolist() ) if x == label ]
def __repr__( self ):
to_return = "Configuration({})\n".format(self.vector)
return to_return
[docs] def map_objects( self, objects ):
"""
Returns a dict of objects sorted according to this configuration.
Args:
objects [list]: A list of objects.
Returns:
sorted_objects [dict]: A dictionary of sorted objects.
"""
if len( objects ) != len( self.vector ):
raise ValueError
sorted_objects = {}
for key in set( self.vector ):
sorted_objects[key] = [ o for k, o in zip( self.vector, objects ) if k == key ]
return sorted_objects
[docs]def as_number( a ):
tot = 0
for num in a:
tot *= 10
tot += int( num )
return tot