Flex arrays

At the core of the array family is the flex submodule, which includes most of the array types, including:

  • flex.int - maps to C++ type int
  • flex.long - maps to C++ type long
  • flex.size_t - maps to C++ type std::size_t
  • flex.bool - maps to C++ type bool
  • flex.double - maps to C++ type double
  • flex.float - maps to C++ type float
  • flex.complex_double - maps to C++ type std::complex
  • flex.std_string - maps to C++ type std::string
  • flex.vec2_double - An array of 2-tuple doubles (e.g. for 2D coordinates)
  • flex.vec3_double - An array of 3-tuple doubles (e.g. for 3D coordinates)
  • flex.vec3_int - An array of 3-tuple ints (e.g. for 3D grid coordinates)
  • flex.sym_mat3_double - An array of 6-tuple doubles. Used to store arrays of symmetric 3x3 matrices

Importing the Flex Array module

To use flex arrays, the module needs to be imported from the CCTBX:

from scitbx.array_family import flex

Creating a flex array

For example, to create an integer flex array, use flex.int().

>>> int_array = flex.int([3,1,2,6])

Display the array content

As the array is an object, simply calling the variable will only print the type.

>>>int_array
<scitbx_array_family_flex_ext.int object at 0x107476ba8>>

Instead, convert the array to a list or a tuple.

>>> int_array = flex.int([3,1,2,6])
>>> list(int_array)
[3, 1, 2, 6]
>>> tuple(int_array)
(3, 1, 2, 6)

Adding elements, extending an array

As in python lists, append and extend allows adding content to the array.

>>> int_array = flex.int([3,1,2,6])
>>> int_array.append(5)
>>> list(int_array)
[3, 1, 2, 6, 5]
>>> int_array.extend(flex.int([12,10]))
>>> list(int_array)
[3, 1, 2, 6, 5, 12, 10]

Getting the array size

int_array = flex.int([3,1,2,6])
>>> len(int_array)
4
>>> int_array.size()
4

Deleting elements and slicing

Delete the element at index 3

>>> int_array = flex.int([3,1,2,6,8,2,6,3,4])
>>> del int_array[3]
>>> list(int_array)
[3, 1, 2, 8, 2, 6, 3, 4]

Slice the array (continuing the example above) from element at index 2 (included) to index 5 (excluded).

>>> list(int_array[2:5])
[2, 8, 2]

Mathematical operations on an array

Mathematical operations like additions, subtraction, division and multiplication can be performed on an array.

>>> a=flex.int([1,2,3])
>>> b=flex.int([2,3,1])
>>> tuple(a+b)
(3, 5, 4)
>>> tuple(a-b)
(-1, -1, 2)
>>> tuple(a*b)
(2, 6, 3)

Note that division on integers follows rules for C++, where the result will always be rounded down.

>>> a=flex.int([1,2,3])
>>> b=flex.int([2,3,1])
>>> tuple(a/b)
(0, 0, 3)

The result above is different than integer division in python:

>>> 1/2
0.5

Dividing the elements one by one can therefore lead to different results than applying the operation to the array:

>>> a=flex.int([1,2,3])
>>> b=flex.int([2,3,1])
>>> tuple(a/b)
(0, 0, 3)
>>> for i,ai in enumerate(a):
>>>   print(ai/b[i])
0.5
0.666666666667
3.0

Some operations won't work on certain array types. For example one cannot take the square root of integers:

>>> a=flex.int([1,2,3])
>>> tuple(flex.sqrt(a))
Traceback (most recent call last):
  File "", line 1, in 
Boost.Python.ArgumentError: Python argument types in
    scitbx_array_family_flex_ext.sqrt(int)
did not match C++ signature:
    sqrt(scitbx::af::versa > >)
    sqrt(scitbx::af::versa > >)

When such an error occurs ("types in ... did not match C++ signature"), it is a hint that the input type is not compatible.

The square root can for example be applied on an array of type double. The array can be either created as type double or an array can be converted to type double using the method as_double().

>>> a = flex.double([4,4,4])
>>> list(flex.sqrt(a))
[2.0, 2.0, 2.0]
>>> a=flex.int([4,4,4])
>>> list(flex.sqrt(a.as_double()))
[2.0, 2.0, 2.0]

Properties of array elements

Flex arrays have methods to get quick access of some element properties:

  • all_eq(n): all elements are equal to n
  • all_ge(n): all elements are greater than or equal to n
  • all_gt(n): all elements are (strictly) greater than n
  • all_le(n): all elements are smaller than or equal to n
  • all_lt(n): all elements are (strictly) smaller than n
  • all_ne(n): all elements are unequal to n
>>> a=flex.int([1,2,3])
>>> a.all_eq(2)
False
>>> b=flex.int([2,2,2,2])
>>> b.all_eq(2)
True
>>> a=flex.double([1.5, 3.6, 7.4, 3, 5])
>>> list(a)
[1.5, 3.6, 7.4, 3.0, 5.0]
>>> a.all_ge(1.5)
True
>>> a.all_gt(1.5)
False
>>> a=flex.int([1,3,5,7,9])
>>> list(a)
[1, 3, 5, 7, 9]
>>> a.all_le(9)
True
>>> a.all_lt(9)
False
>>> a=flex.int([1,2,3])
>>> a.all_ne(4)
True
>>> a.all_ne(3)
False

Multidimensional arrays

The data in a flex array is stored in a contiguous one-dimensional sequence of memory. Multidimensional flex arrays up to 10 dimensions are supported. An accessor (of type flex.grid) specifies how the underlying one-dimensional data array should be interpreted as a multidimensional array.

Creating the accessor (grid) for a 1d array:

>>> a = flex.double(range(9))
>>> list(a)
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]
>>> grid = a.accessor()
>>> grid.nd() # the number of dimensions
1
>>> grid.all()
(9,)

Create a multidimensional array using the reshape() method.

>>> a = flex.double(range(9))
>>> a.reshape(flex.grid(3,3))
>>> a.nd()
2
>>> a.all()
(3, 3)

Note that the reshape() method only works if the dimensions are consistent.

>>> a = flex.double(range(9))
>>> a.reshape(flex.grid(3,2))
Traceback (most recent call last):
  File "", line 1, in 
RuntimeError: scitbx Internal Error: .../phenix_svn/modules/cctbx_project/scitbx/array_family/boost_python/flex_wrapper.h(449):
SCITBX_ASSERT(grid.size_1d() == a.size()) failure.

Multidimensional flex arrays are stored in row-major order (the consecutive elements of a row reside next to each other) and elements can be accessed using the Python square bracket notation:

>>> c = flex.int(range(6))
>>> list(c)
[0, 1, 2, 3, 4, 5]
>>> c.nd()
1
>>> c.reshape(flex.grid(2,3))
>>> list(c)
[0, 1, 2, 3, 4, 5]
>>> c.nd()
2
>>> for i in range(c.all()[0]):
...   for j in range(c.all()[1]):
...     print(c[i,j]),
...   print
...
0 1 2
3 4 5

Note that for the initial and the reshaped array, list(c) will print a one dimensional list in each case.

It is also worth noting that vec2_double and vec3_double are one dimensional arrays:

>>> c = flex.double([(1.5,2,3), (4,5,6)])
>>> c.nd()
2
>>> d = flex.vec3_double([(1.5,2,3), (4,5,6)])
>>> d.nd()
1

Conversion of array type

The flex module has several methods to convert the type of an array.

  • as_1d
  • as_bool
  • as_double
  • as_long
  • as_numpy_array
  • as_rgb_scale_string
  • as_string
>>> a=flex.int([1,2,3])
>>> b=flex.int([2,3,1])