| 135 | | |
| 136 | | def distance(src, tgt, mask=None, scale_factor=1.0, offset=0.0, |
| 137 | | periodic_boundaries=None): # may need to add an offset parameter |
| 138 | | """ |
| 139 | | Return the Euclidian distance between two cells. |
| 140 | | `mask` allows only certain dimensions to be considered, e.g.:: |
| 141 | | * to ignore the z-dimension, use `mask=array([0,1])` |
| 142 | | * to ignore y, `mask=array([0,2])` |
| 143 | | * to just consider z-distance, `mask=array([2])` |
| 144 | | `scale_factor` allows for different units in the pre- and post- position |
| 145 | | (the post-synaptic position is multipied by this quantity). |
| 146 | | """ |
| 147 | | d = src.position - scale_factor*(tgt.position + offset) |
| 148 | | |
| 149 | | if not periodic_boundaries == None: |
| 150 | | d = numpy.minimum(abs(d), periodic_boundaries-abs(d)) |
| 151 | | if mask is not None: |
| 152 | | d = d[mask] |
| 153 | | return numpy.sqrt(numpy.dot(d, d)) |
| 154 | | |
| 155 | | |
| 156 | | class Space(object): |
| 157 | | |
| 158 | | AXES = {'x' : [0], 'y': [1], 'z': [2], |
| 159 | | 'xy': [0,1], 'yz': [1,2], 'xz': [0,2], 'xyz': range(3), None: range(3)} |
| 160 | | |
| 161 | | def __init__(self, axes=None, scale_factor=1.0, offset=0.0, |
| 162 | | periodic_boundaries=None): |
| 163 | | """ |
| 164 | | axes -- if not supplied, then the 3D distance is calculated. If supplied, |
| 165 | | axes should be a string containing the axes to be used, e.g. 'x', or |
| 166 | | 'yz'. axes='xyz' is the same as axes=None. |
| 167 | | scale_factor -- it may be that the pre and post populations use |
| 168 | | different units for position, e.g. degrees and µm. In this case, |
| 169 | | `scale_factor` can be specified, which is applied to the positions |
| 170 | | in the post-synaptic population. |
| 171 | | offset -- if the origins of the coordinate systems of the pre- and post- |
| 172 | | synaptic populations are different, `offset` can be used to adjust |
| 173 | | for this difference. The offset is applied before any scaling. |
| 174 | | periodic_boundaries -- either `None`, or a tuple giving the boundaries |
| 175 | | for each dimension, e.g. `((x_min, x_max), None, (z_min, z_max))`. |
| 176 | | """ |
| 177 | | self.periodic_boundaries = periodic_boundaries |
| 178 | | self.axes = numpy.array(Space.AXES[axes]) |
| 179 | | self.scale_factor = scale_factor |
| 180 | | self.offset = offset |
| 181 | | |
| 182 | | def distances(self, A, B): |
| 183 | | """ |
| 184 | | Calculate the distance matrix between two sets of coordinates, given |
| 185 | | the topology of the current space. |
| 186 | | From http://projects.scipy.org/pipermail/numpy-discussion/2007-April/027203.html |
| 187 | | """ |
| 188 | | if len(A.shape) == 1: |
| 189 | | A = A.reshape(3, 1) |
| 190 | | if len(B.shape) == 1: |
| 191 | | B = B.reshape(3, 1) |
| 192 | | B = self.scale_factor*(B + self.offset) |
| 193 | | d = numpy.zeros((A.shape[1], B.shape[1]), dtype=A.dtype) |
| 194 | | for axis in self.axes: |
| 195 | | diff2 = A[axis,:,None] - B[axis,:] |
| 196 | | if self.periodic_boundaries is not None: |
| 197 | | boundaries = self.periodic_boundaries[axis] |
| 198 | | if boundaries is not None: |
| 199 | | range = boundaries[1]-boundaries[0] |
| 200 | | ad2 = abs(diff2) |
| 201 | | diff2 = numpy.minimum(ad2, range-ad2) |
| 202 | | diff2 **= 2 |
| 203 | | d += diff2 |
| 204 | | numpy.sqrt(d, d) |
| 205 | | return d |