I'm writing a hash function to create hashes of some given size (e.g. 20 bits).
I have learnt how to write the hashes to files in a binary form (see my related question here), but now I would like to handle these hashes in Python (2.7) using the minimum memory allocation. Right now they are typed as int, so they are allocated 24 bytes each, which is huge for a 20 bits object.
How can I create a custom Python object of arbitrary size (e.g. in my case 3 bytes)?
You could do something like you want by packing the bits for each object into a packed array of bit (or boolean) values. There are a number of existing Python bitarray extension modules available. Implementing a higher level "array of fixed bit width integer values" with one is a relatively straight-forward process.
Here's an example based on one in pypi that's implemented in C for speed. You can also download an unofficial pre-built Windows version of it, created by Christoph Gohlke, from here.
Updated —
Now works in Python 2.7 & 3.x.
from __future__ import print_function
# uses https://pypi.python.org/pypi/bitarray
from bitarray import bitarray as BitArray
try:
from functools import reduce # Python 3.
except:
pass
class PackedIntArray(object):
""" Packed array of unsigned fixed-bit-width integer values. """
def __init__(self, array_size, item_bit_width, initializer=None):
self.array_size = array_size
self.item_bit_width = item_bit_width
self.bitarray = BitArray(array_size * item_bit_width)
if initializer is not None:
try:
iter(initializer)
except TypeError: # not iterable
self.bitarray.setall(initializer) # set all to bool(initializer)
else:
for i in xrange(array_size):
self[i] = initializer[i] # must be same length as array
def __getitem__(self, index):
offset = index * self.item_bit_width
bits = self.bitarray[offset: offset+self.item_bit_width]
return reduce(lambda x, y: (x << 1) | y, bits, 0)
def __setitem__(self, index, value):
bits = BitArray('{:0{}b}'.format(value, self.item_bit_width))
offset = index * self.item_bit_width
self.bitarray[offset: offset+self.item_bit_width] = bits
def __len__(self):
""" Return the number of items stored in the packed array.. """
return self.array_size
def length(self):
""" Return the number of bits stored in the bitarray.. """
return self.bitarray.length()
def __repr__(self):
return('PackedIntArray({}, {}, ('.format(self.array_size,
self.item_bit_width) +
', '.join((str(self[i]) for i in xrange(self.array_size))) +
'))')
if __name__ == '__main__':
from random import randrange
# hash function configuration
BW = 8, 8, 4 # bit widths of each integer
HW = sum(BW) # total hash bit width
def myhash(a, b, c):
return (((((a & (2**BW[0]-1)) << BW[1]) |
b & (2**BW[1]-1)) << BW[2]) |
c & (2**BW[2]-1))
hashes = PackedIntArray(3, HW)
print('hash bit width: {}'.format(HW))
print('length of hashes array: {:,} bits'.format(hashes.length()))
print()
print('populate hashes array:')
for i in range(len(hashes)):
hashed = myhash(*(randrange(2**bit_width) for bit_width in BW))
print(' hashes[{}] <- {:,} (0b{:0{}b})'.format(i, hashed, hashed, HW))
hashes[i] = hashed
print()
print('contents of hashes array:')
for i in range(len(hashes)):
print((' hashes[{}]: {:,} '
'(0b{:0{}b})'.format(i, hashes[i], hashes[i], HW)))
Sample output:
hash bit width: 20
length of hashes array: 60 bits
populate hashes array:
hashes[0] <- 297,035 (0b01001000100001001011)
hashes[1] <- 749,558 (0b10110110111111110110)
hashes[2] <- 690,468 (0b10101000100100100100)
contents of hashes array:
hashes[0]: 297,035 (0b01001000100001001011)
hashes[1]: 749,558 (0b10110110111111110110)
hashes[2]: 690,468 (0b10101000100100100100)
Note: bitarray.bitarray objects also have methods to write and read their bits to and from files. These could be used to also provide similar functionality to the PackedIntArray class above.
Related
I am trying to sort items that have sizes described by two numbers like the following
10 x 13
100 x 60
7 x 8
The size is saved as a string. I want them sorted like this (first by first dimension, then by second dimension)
7 x 8
10 x 13
100 x 60
how can this be achieved with Django? It would be nice if we could somehow use
Item.objects.sort
I would advice not to store these as a string, but as two IntegerFields, for example, with:
class Item(models.Model):
width = models.IntegerField()
height = models.IntegerField()
#property
def size(self):
return f'{self.width}x{self.height}'
#size.setter
def size(self, value):
self.width, self.height = map(int, value.split('x'))
Then you can easily sort by Item.objects.order_by('width', 'height') for example. We thus have a property .size that can format the item to a size, and even with a setter that can "parse" the value and put the width and height in the corresponding fields.
you could use sorted for this from python math library. Had a similar problem way back this is what I used and it worked just fine.
import math
l = ['10x13', '100x60','7x8']
sorted(l, key=lambda dim: math.hypot(*map(int, dim.split('x'))))
# ['7x8', '10x13', '100x60']
New to pyomo and python in general and I am trying to implement a simple solution to a binary integer programming problem. However the problem is large but a large percentage of the values of the matrix x are known in advance. I have been trying to figure out how to 'tell' pyomo that some values are known in advance and what they are.
from __future__ import division # converts to float before division
from pyomo.environ import * # Make symbolds used by pyomo known to python
model = AbstractModel() # Declaration of an abstract model, called model
model.users = Set()
model.slots = Set()
model.prices=Param(model.users, model.slots)
model.users_balance=Param(model.users)
model.slot_bounds=Param(model.slots)
model.x = Var(model.users, model.slots, domain=Binary)
# Define the objective function
def obj_expression(model):
return sum(sum(model.prices[i,j] * model.x[i,j] for i in model.users)
for j in model.slots)
model.OBJ = Objective(rule=obj_expression, sense=maximize)
# A user can only be assigned to one slot
def one_slot_rule(model, users):
return sum(model.x[users,n] for n in model.slots) <= 1
model.OneSlotConstraint = Constraint(model.users, rule=one_slot_rule)
# Certain slots have a minimum balance requirement.
def min_balance_rule1(model, slots):
return sum(model.x[n,slots] * model.users_balance[n] for n in
model.users) >= model.slot_bounds[slots]
model.MinBalanceConstraint1 = Constraint(model.slots,
rule=min_balance_rule1)
So I want to be able to benefit from the fact that I know certain values of x[i,j] to be 0. So for example I have a list of extra conditions
x[1,7] = 0
x[3,6] = 0
x[5,8] = 0
How do I include this information in order to benefit from reducing the search space?
Many Thanks.
After the model is constructed you can do the following:
model.x[1,7].fix(0)
model.x[3,6].fix(0)
model.x[5,8].fix(0)
or, assuming that you have a Set, model.Arcs, that contains the following:
model.Arcs = Set(initialize=[(1,7), (3,6), (5,8)])
you can fix x variables in a loop:
for i,j in model.Arcs:
model.x[i,j].fix(0)
I can observe a HUGE time difference between python 2.7 and 3.5 running this piece of code. It seems due to my shared object _SharedProgress but i can't figure why python 3.5 (12s to run) is so much slower than 2.7 (1s to run).
Indeed if i comment progress.update(), perfos are almost identical (3.5 remains a bit slower).
Can someone explain me why? :)
Of course i would like to keep 2.7 perfo with 3.5...
from __future__ import print_function
from multiprocessing import Process
from multiprocessing.managers import BaseManager
from time import time
class _SharedProgress(object):
current = 0
def get(self):
return self.current
def update(self, new_value=1):
self.current += new_value
class _GlobalManager(BaseManager):
BaseManager.register('SharedProgress', _SharedProgress)
class WorkManager:
def __init__(self, nbWorkers, workerTask):
self.manager = _GlobalManager()
self.sharedProgress = None
self.totalProgress = nbWorkers * 100
self.pool = []
start = time()
self.manager.start()
self.sharedProgress = self.manager.SharedProgress()
inputs = [(self.sharedProgress,) for _ in range(nbWorkers)]
processToLaunch = [i for i in range(nbWorkers)]
for i in processToLaunch:
self.pool.append(Process(target=workerTask, args=inputs[i]))
while processToLaunch or any((w.is_alive() for w in self.pool)):
if processToLaunch:
self.pool[processToLaunch.pop(0)].start()
if self.sharedProgress.get() == self.totalProgress:
break
print("DONE in {}!".format(time() - start))
def __workerTask(progress):
prevPercent, current, currentPercent, total = 0, 0, 0, 10000
for i in range(total):
current += 1
currentPercent = (current * 100) / total
if currentPercent != prevPercent:
progress.update(currentPercent - prevPercent) # IF I COMMENT THIS LINE, PERFOS ARE ALMOST IDENTICAL
prevPercent = currentPercent
if __name__ == '__main__':
WorkManager(10, __workerTask)
The main difference comes from the division. In Python 3, dividing two integer values with / will always yield a float, in Python 2 it remained an int. You can force the python2 behavior for both versions by using //:
currentPercent = (current * 100) // total
Or the python3 behavior by initializing current = 0.. There still remains some performance gap, which might be caused by the different int types in Python 2 and 3. Python 2 used to have separate int and long types, while Python 3 only has one unified int type which covers both. If you force Python 2 to use long (current = 0L), it becomes even slower than the Python 3 version.
As Zulan noted,
currentPercent = (current * 100) // total
fixes the issue, but the performance issue is not directly related to integer or floating point division.
When performing a floating point division, currentPercent is always different from prevPercent, so progress bar is updated at every iteration, which is considerable redraw work, whereas with integer division, it's just updated 100 times.
This overfrequent redraw is the actual cause of the slowdowns.
How can I interact with the values of a generator expression? For instance:
def sumValues(*args):
# compute the sum of values
sumValues(abs(x) for x in range(0,10))
When the range is known (e.g., we know the range is [0,10)), we can put a for loop, but what if the range is unknown?
it depend on how you want your function to be called, if you want to call it with list, generator, or more generally a iterable (anything that can be used in a for-loop) and imitate the behavior of the build-in sum then
def my_sum(iterable):
total = 0
for x in iterable:
total += x
return total
and use like
my_sum(range(10))
my_sum(abs(x) for x in range(0,10))
my_sum([1,2,3,4,5,6])
if you want to imitate the behavior of max and use it as above and also like my_sum(1,2), my_sum(1,2,3), my_sum(1,2,3,4,5,6,7), etc. then
def my_sum(*argv):
total = 0
values = None
if len(argv) == 1:
values = argv[0]
else:
values = argv
for x in values:
total += x
return total
I figured. Here's the answer:
def sumValues(*args):
# compute the sum of values
total = 0
for item in args[0]:
total += item
return total
print(sumValues(abs(x) for x in range(0,10)))
[output] => 45
I am using a Python (via ctypes) wrapped C library to run a series of computation. At different stages of the running, I want to get data into Python, and specifically numpy arrays.
The wrapping I am using does two different types of return for array data (which is of particular interest to me):
ctypes Array: When I do type(x) (where x is the ctypes array, I get a <class 'module_name.wrapper_class_name.c_double_Array_12000'> in return. I know that this data is a copy of the internal data from the documentation and I am able to get it into a numpy array easily:
>>> np.ctypeslib.as_array(x)
This returns a 1D numpy array of the data.
ctype pointer to data: In this case from the library's documentation, I understand that I am getting a pointer to the data stored and used directly to the library. Whey I do type(y) (where y is the pointer) I get <class 'module_name.wrapper_class_name.LP_c_double'>. With this case I am still able to index through the data like y[0][2], but I was only able to get it into numpy via a super awkward:
>>> np.frombuffer(np.core.multiarray.int_asbuffer(
ctypes.addressof(y.contents), array_length*np.dtype(float).itemsize))
I found this in an old numpy mailing list thread from Travis Oliphant, but not in the numpy documentation. If instead of this approach I try as above I get the following:
>>> np.ctypeslib.as_array(y)
...
... BUNCH OF STACK INFORMATION
...
AttributeError: 'LP_c_double' object has no attribute '__array_interface__'
Is this np.frombuffer approach the best or only way to do this? I am open to other suggestions but must would still like to use numpy as I have a lot of other post-processing code that relies on numpy functionality that I want to use with this data.
Creating NumPy arrays from a ctypes pointer object is a problematic operation. It is unclear who actually owns the memory the pointer is pointing to. When will it be freed again? How long is it valid? Whenever possible I would try to avoid this kind of construct. It is so much easier and safer to create arrays in the Python code and pass them to the C function than to use memory allocated by a Python-unaware C function. By doing the latter, you negate to some extent the advantages of having a high-level language taking care of the memory management.
If you are really sure that someone takes care of the memory, you can create an object exposing the Python "buffer protocol" and then create a NumPy array using this buffer object. You gave one way of creating the buffer object in your post, via the undocumented int_asbuffer() function:
buffer = numpy.core.multiarray.int_asbuffer(
ctypes.addressof(y.contents), 8*array_length)
(Note that I substituted 8 for np.dtype(float).itemsize. It's always 8, on any platform.) A different way to create the buffer object would be to call the PyBuffer_FromMemory() function from the Python C API via ctypes:
buffer_from_memory = ctypes.pythonapi.PyBuffer_FromMemory
buffer_from_memory.restype = ctypes.py_object
buffer = buffer_from_memory(y, 8*array_length)
For both these ways, you can create a NumPy array from buffer by
a = numpy.frombuffer(buffer, float)
(I actually do not understand why you use .astype() instead of a second parameter to frombuffer; furthermore, I wonder why you use np.int, while you said earlier that the array contains doubles.)
I'm afraid it won't get much easier than this, but it isn't that bad, don't you think? You could bury all the ugly details in a wrapper function and don't worry about it any more.
Another possibility (which may require more recent versions of libraries than is available when the first answer was written -- I tested something similar with ctypes 1.1.0 and numpy 1.5.0b2) is to convert from the pointer to the array.
np.ctypeslib.as_array(
(ctypes.c_double * array_length).from_address(ctypes.addressof(y.contents)))
This seems to still have the shared ownership semantics, so you probably need to make sure that you free the underlying buffer eventually.
Neither of these worked for me in Python 3. As a general solution for converting a ctypes pointer into a numpy ndarray in python 2 and 3 I found this worked (via getting a read-only buffer):
def make_nd_array(c_pointer, shape, dtype=np.float64, order='C', own_data=True):
arr_size = np.prod(shape[:]) * np.dtype(dtype).itemsize
if sys.version_info.major >= 3:
buf_from_mem = ctypes.pythonapi.PyMemoryView_FromMemory
buf_from_mem.restype = ctypes.py_object
buf_from_mem.argtypes = (ctypes.c_void_p, ctypes.c_int, ctypes.c_int)
buffer = buf_from_mem(c_pointer, arr_size, 0x100)
else:
buf_from_mem = ctypes.pythonapi.PyBuffer_FromMemory
buf_from_mem.restype = ctypes.py_object
buffer = buf_from_mem(c_pointer, arr_size)
arr = np.ndarray(tuple(shape[:]), dtype, buffer, order=order)
if own_data and not arr.flags.owndata:
return arr.copy()
else:
return arr
np.ctypeslib.as_array is all you need here.
From an array:
c_arr = (c_float * 8)()
np.ctypeslib.as_array(c_arr)
From a pointer
c_arr = (c_float * 8)()
ptr = ctypes.pointer(c_arr[0])
np.ctypeslib.as_array(ptr, shape=(8,))
Using np.ndarrays as ctypes arguments
The preferable approach is using ndpointer, as mentioned in the numpy-docs.
This approach is more flexible than using, for example,
POINTER(c_double), since several restrictions can be specified, which
are verified upon calling the ctypes function. These include data
type, number of dimensions, shape and flags. If a given array does not
satisfy the specified restrictions, a TypeError is raised.
Minimal, Reproducible Example
Calling memcpy from python. Eventually the filename of the standard C-library libc.so.6 needs to be adjusted.
import ctypes
import numpy as np
n_bytes_f64 = 8
nrows = 2
ncols = 5
clib = ctypes.cdll.LoadLibrary("libc.so.6")
clib.memcpy.argtypes = [
np.ctypeslib.ndpointer(dtype=np.float64, ndim=2, flags='C_CONTIGUOUS'),
np.ctypeslib.ndpointer(dtype=np.float64, ndim=1, flags='C_CONTIGUOUS'),
ctypes.c_size_t]
clib.memcpy.restype = ctypes.c_void_p
arr_from = np.arange(nrows * ncols).astype(np.float64)
arr_to = np.empty(shape=(nrows, ncols), dtype=np.float64)
print('arr_from:', arr_from)
print('arr_to:', arr_to)
print('\ncalling clib.memcpy ...\n')
clib.memcpy(arr_to, arr_from, nrows * ncols * n_bytes_f64)
print('arr_from:', arr_from)
print('arr_to:', arr_to)
Output
arr_from: [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
arr_to: [[0.0e+000 4.9e-324 9.9e-324 1.5e-323 2.0e-323]
[2.5e-323 3.0e-323 3.5e-323 4.0e-323 4.4e-323]]
calling clib.memcpy ...
arr_from: [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
arr_to: [[0. 1. 2. 3. 4.]
[5. 6. 7. 8. 9.]]
If you modify the ndim=1/2 arguments of ndpointer to be inconsistent with the dimensions of arr_from/arr_to, the code fails with an ArgumentError.
As the title of this question is quite general, ...
Constructing a np.ndarray from a ctypes.c_void_p result
Minimal, Reproducible Example
In the following example, some memory is allocated by malloc and filled with 0s by memset. Then a numpy array is constructed, to access this memory. Of course the occur some ownership issues, as python will not free memory, which was allocated in c. To avoid memory leaks, one has to free the allocated memory again by ctypes. The copy method can be used for the np.ndarray to acquire ownership.
import ctypes
import numpy as np
n_bytes_int = 4
size = 7
clib = ctypes.cdll.LoadLibrary("libc.so.6")
clib.malloc.argtypes = [ctypes.c_size_t]
clib.malloc.restype = ctypes.c_void_p
clib.memset.argtypes = [
ctypes.c_void_p,
ctypes.c_int,
ctypes.c_size_t]
clib.memset.restype = np.ctypeslib.ndpointer(
dtype=np.int32, ndim=1, flags='C_CONTIGUOUS')
clib.free.argtypes = [ctypes.c_void_p]
clib.free.restype = ctypes.c_void_p
pntr = clib.malloc(size * n_bytes_int)
ndpntr = clib.memset(pntr, 0, size * n_bytes_int)
print(type(ndpntr))
ctypes_pntr = ctypes.cast(ndpntr, ctypes.POINTER(ctypes.c_int))
print(type(ctypes_pntr))
print()
arr_noowner = np.ctypeslib.as_array(ctypes_pntr, shape=(size,))
arr_owner = np.ctypeslib.as_array(ctypes_pntr, shape=(size,)).copy()
# arr_owner = arr_noowner.copy()
print('arr_noowner (at {:}): {:}'.format(arr_noowner.ctypes.data, arr_noowner))
print('arr_owner (at {:}): {:}'.format(arr_owner.ctypes.data, arr_owner))
print('\nfree allocated memory again ...\n')
_ = clib.free(pntr)
print('arr_noowner (at {:}): {:}'.format(arr_noowner.ctypes.data, arr_noowner))
print('arr_owner (at {:}): {:}'.format(arr_owner.ctypes.data, arr_owner))
print('\njust for fun: free some python-memory ...\n')
_ = clib.free(arr_owner.ctypes.data_as(ctypes.c_void_p))
print('arr_noowner (at {:}): {:}'.format(arr_noowner.ctypes.data, arr_noowner))
print('arr_owner (at {:}): {:}'.format(arr_owner.ctypes.data, arr_owner))
Output
<class 'numpy.ctypeslib.ndpointer_<i4_1d_C_CONTIGUOUS'>
<class '__main__.LP_c_int'>
arr_noowner (at 104719884831376): [0 0 0 0 0 0 0]
arr_owner (at 104719884827744): [0 0 0 0 0 0 0]
free allocated memory again ...
arr_noowner (at 104719884831376): [ -7687536 24381 -28516336 24381 0 0 0]
arr_owner (at 104719884827744): [0 0 0 0 0 0 0]
just for fun: free some python-memory ...
arr_noowner (at 104719884831376): [ -7687536 24381 -28516336 24381 0 0 0]
arr_owner (at 104719884827744): [ -7779696 24381 -28516336 24381 0 0 0]
If you are ok with creating arrays in python, the following example with 2d array works in python3:
import numpy as np
import ctypes
OutType = (ctypes.c_float * 4) * 6
out = OutType()
YourCfunction = ctypes.CDLL('./yourlib.so').voidreturningfunctionwithweirdname
YourCfunction.argtypes = [ctypes.POINTER(ctypes.c_float)]*3, ctypes.POINTER(ctypes.c_float)]*5, OutType]
YourCfunction(input1, input2, out)
out = np.array(out) # convert it to numpy
print(out)
numpy and ctypes versions are 1.11.1 and 1.1.0