Commit d5a1e6b9 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Rename millibenchmarks to minibenchmarks; implement filter() and enumerate()

parent 03103602
# From: https://mail.python.org/pipermail/pypy-dev/2014-August/012695.html
L = 10
xrows = range(L)
xcols = range(L)
bitmap = [0] * L ** 2
poss = [(i, j) for i in xrows for j in xcols]
idx_to_pos = dict()
pos_to_idx = dict()
for i, pos in enumerate(poss):
idx_to_pos[i] = pos
pos_to_idx[pos] = i
# rows, columns, "right" diagonals and "left" diagonals
poscols = [[(i, j) for i in xrows] for j in xcols]
posrows = [[(i, j) for j in xcols] for i in xrows]
posdiag = [[(h, g - h) for h in range(g + 1) if h < L and g - h < L] for g in range(L * 2 - 1)]
posgaid = [[(g + h, h) for h in range(L) if -1 < g + h < L] for g in range(-L + 1, L)]
def attacks(pos):
""" all attacked positions """
row = filter(lambda r: pos in r, posrows)
col = filter(lambda c: pos in c, poscols)
dia = filter(lambda d: pos in d, posdiag)
gai = filter(lambda g: pos in g, posgaid)
assert len(row) == len(col) == len(dia) == len(gai) == 1
return frozenset(row[0]), frozenset(col[0]), frozenset(dia[0]), frozenset(gai[0])
attackmap = {(i, j): attacks((i, j)) for i in range(L) for j in range(L)}
setcols = set(map(frozenset, poscols))
setrows = set(map(frozenset, posrows))
setdiag = set(map(frozenset, posdiag))
setgaid = set(map(frozenset, posgaid))
# choice between bitmaps and sets
#
# bitmaps are reresented natively as (long) ints in Python,
# thus bitmap operations are very very fast
#
# however for asymptotic complexity, x in bitmap operation is O(N) and x in set is O(logN)
#
# in my experience python function calls are expensive, thus the threshold where sets show benefit is rather high
# another possible explanation for high threshold is large memory size of Python dictionaries and thus frozensets,
# __sizeof__ for representaions of range(100):: set: 8K, frozenset: 4K, (2 ** 100): 40 bytes
#
# for 8x8 board, a 64-bit bitmap wins by a large margin
# IMO 10x10 board is still faster with bitmaps
# all queens are equivalent, thus solution (Q1, Q2, Q3) == (Q1, Q3, Q2)
# let's order queens, so that Q1 always preceeds on Q2 on the board
# then, let's do an exhaustive search with early pruning:
# consider board of 4 [ , , , ] for 3 queens
# position [ , ,Q1, ] will never generate a solution, because there's no space for both Q2 and Q3 left
# likewise, let's extend concept of "space" along 4 dimensions -- rows, cols, diag, gaid
solutions = []
def place(board, queens, r, c, d, g):
"""
remaining unattacked places on the board
remaining queens to place
remaining rows, cols, diag, gaid free
"""
# if we are ran out of queens, it's a valid solution
if not queens:
# print "solution found"
solutions.append(None)
# early pruning, make sure this many queens can actually be placed
if len(queens) > len(board): return
if len(queens) > len(r): return
if len(queens) > len(c): return
if len(queens) > len(d): return
if len(queens) > len(g): return
# queens[0] is queen to be places on some pos
for ip, pos in enumerate(board):
ar, ac, ad, ag = attackmap[pos]
attacked = frozenset.union(ar, ac, ad, ag)
nboard = [b for b in board[ip + 1:] if b not in attacked]
place(nboard, queens[1:], r - ar, c - ac, d - ad, g - ag)
def run():
del solutions[:]
place(poss, sorted(["Q%s" % i for i in range(L)]), setrows, setcols, setdiag, setgaid)
return len(solutions)
print(run())
......@@ -707,7 +707,7 @@ define make_search
$(eval \
$1: ../test/tests/$1 ;
$1: ../microbenchmarks/$1 ;
$1: ../millibenchmarks/$1 ;
$1: ../minibenchmarks/$1 ;
$1: ../benchmarks/$1 ;
)
endef
......
......@@ -359,6 +359,17 @@ Box* map2(Box* f, Box* container) {
return rtn;
}
Box* filter2(Box* f, Box* container) {
Box* rtn = new BoxedList();
for (Box* e : container->pyElements()) {
Box* r = runtimeCall(f, ArgPassSpec(1), e, NULL, NULL, NULL, NULL);
bool b = nonzero(r);
if (b)
listAppendInternal(rtn, e);
}
return rtn;
}
Box* zip2(Box* container1, Box* container2) {
BoxedList* rtn = new BoxedList();
......@@ -437,6 +448,66 @@ static BoxedClass* makeBuiltinException(BoxedClass* base, const char* name) {
return cls;
}
BoxedClass* enumerate_cls;
extern const ObjectFlavor enumerate_flavor;
class BoxedEnumerate : public Box {
private:
Box* iterator;
int64_t idx;
public:
BoxedEnumerate(Box* iterator, int64_t idx) : Box(&enumerate_flavor, enumerate_cls), iterator(iterator), idx(idx) {}
static Box* new_(Box* cls, Box* obj, Box* start) {
RELEASE_ASSERT(cls == enumerate_cls, "");
RELEASE_ASSERT(start->cls == int_cls, "");
int64_t idx = static_cast<BoxedInt*>(start)->n;
static const std::string iter_name("__iter__");
Box* iterator = callattrInternal(obj, &iter_name, CLASS_ONLY, NULL, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
if (!iterator)
raiseNotIterableError(getTypeName(iterator)->c_str());
return new BoxedEnumerate(iterator, idx);
}
static Box* iter(Box* _self) {
assert(_self->cls == enumerate_cls);
BoxedEnumerate* self = static_cast<BoxedEnumerate*>(_self);
return self;
}
static Box* next(Box* _self) {
static const std::string next_name("next");
assert(_self->cls == enumerate_cls);
BoxedEnumerate* self = static_cast<BoxedEnumerate*>(_self);
Box* o = callattrInternal(self->iterator, &next_name, CLASS_ONLY, NULL, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
RELEASE_ASSERT(o, "");
Box* r = new BoxedTuple({boxInt(self->idx), o});
self->idx++;
return r;
}
static Box* hasnext(Box* _self) {
static const std::string hasnext_name("__hasnext__");
assert(_self->cls == enumerate_cls);
BoxedEnumerate* self = static_cast<BoxedEnumerate*>(_self);
Box* r = callattrInternal(self->iterator, &hasnext_name, CLASS_ONLY, NULL, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
RELEASE_ASSERT(r, "%s", getTypeName(self->iterator)->c_str());
return r;
}
static void gcHandler(GCVisitor* v, void* p) {
boxGCHandler(v, p);
BoxedEnumerate* it = (BoxedEnumerate*)p;
v->visit(it->iterator);
}
};
const ObjectFlavor enumerate_flavor(&BoxedEnumerate::gcHandler, NULL);
void setupBuiltins() {
builtins_module = createModule("__builtin__", "__builtin__");
......@@ -515,6 +586,17 @@ void setupBuiltins() {
Box* issubclass_obj = new BoxedFunction(boxRTFunction((void*)issubclass_func, BOXED_BOOL, 2));
builtins_module->giveAttr("issubclass", issubclass_obj);
enumerate_cls = new BoxedClass(object_cls, 0, sizeof(BoxedEnumerate), false);
enumerate_cls->giveAttr("__name__", boxStrConstant("enumerate"));
enumerate_cls->giveAttr("__new__", new BoxedFunction(boxRTFunction((void*)BoxedEnumerate::new_, UNKNOWN, 3, 1, false, false), { boxInt(0) }));
enumerate_cls->giveAttr("__iter__", new BoxedFunction(boxRTFunction((void*)BoxedEnumerate::iter, typeFromClass(enumerate_cls), 1)));
enumerate_cls->giveAttr("next", new BoxedFunction(boxRTFunction((void*)BoxedEnumerate::next, typeFromClass(enumerate_cls), 1)));
enumerate_cls->giveAttr("__hasnext__", new BoxedFunction(boxRTFunction((void*)BoxedEnumerate::hasnext, typeFromClass(enumerate_cls), 1)));
enumerate_cls->freeze();
builtins_module->giveAttr("enumerate", enumerate_cls);
CLFunction* sorted_func = createRTFunction(1, 0, false, false);
addRTFunction(sorted_func, (void*)sortedList, LIST, { LIST });
addRTFunction(sorted_func, (void*)sorted, LIST, { UNKNOWN });
......@@ -534,6 +616,7 @@ void setupBuiltins() {
builtins_module->giveAttr("open", open_obj);
builtins_module->giveAttr("map", new BoxedFunction(boxRTFunction((void*)map2, LIST, 2)));
builtins_module->giveAttr("filter", new BoxedFunction(boxRTFunction((void*)filter2, LIST, 2)));
builtins_module->giveAttr("zip", new BoxedFunction(boxRTFunction((void*)zip2, LIST, 2)));
builtins_module->giveAttr("dir", new BoxedFunction(boxRTFunction((void*)dir, LIST, 1, 1, false, false), { NULL }));
builtins_module->giveAttr("object", object_cls);
......
......@@ -28,3 +28,7 @@ class C(object):
print sum([C(1), C(2), C(3)], C(4)).n
print zip([1, 2, 3, 0], ["one", "two", "three"])
print filter(lambda x: x % 2, xrange(20))
print type(enumerate([]))
print list(enumerate(xrange(5, 10)))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment