how to compose n-dimensional slices to get a cannonical ndslice - python

Consider a portion of an ndarray x formed by two consecutive slices (I'm using numpy in the example, but the question is more general. I'm actually using pytorch in my application.):
import numpy as np
x = np.arange(4 * 10 * 12 * 7).reshape(4, 10, 12, 7)
first = (slice(None), 3, slice(3, 9))
second = (2, slice(1, 3), slice(5))
out = x[first][second]
I want a way to get a canonical, combined ndslice as a function of x.shape, first and second. E.g.
combined = compose(x.shape, first, second)
assert np.equal(x[first][second], x[combined]).all()
assert combined == (2, 3, slice(4, 6), slice(5))
I am only interested in "simple" ndslices consisting of: a single int, a single slice object, or a tuple of any combination of ints and slice objects.
By canonical, I mean that the resulting combined slice should uniquely identify that subset of x.
For example here is another way to access the same segment and it should lead to the same combined ndslice:
other_first = (slice(2, 4), slice(None), slice(2, 7))
other_second = (0, 3, slice(2, -1), slice(5))
combined = compose(x.shape, other_first, other_second)
assert np.equal(x[other_first][other_second], x[combined]).all()
assert combined == (2, 3, slice(4, 6), slice(5))
Because slices support "None" and negative indexes, we will need the shape of x in order to get a canonical ndslice.
Related Questions
Note that I am interested in x[first][second] which different (in general) from x[first, second] discussed elsewhere.

I'm still interested if someone happens to know of a builtin or more elegant solution (even if specific to numpy or pytorch), but this is the general, home-grown solution I came up with:
def compose_single(lhs, rhs, length):
out = range(length)[lhs][rhs]
return out if isinstance(out, int) else slice(out.start, out.stop, out.step)
def compose(shape, first, second):
def ensure_tuple(ndslice):
return ndslice if isinstance(ndslice, tuple) else (ndslice,)
first = ensure_tuple(first)
second = ensure_tuple(second)
out = list(first) + [slice(None)] * (len(shape) - len(first))
remaining_dims = [i for i, s in enumerate(out) if isinstance(s, slice)]
for i, rhs in zip(remaining_dims, second):
out[i] = compose_single(out[i], rhs, length=shape[i])
return tuple(out)
Note that the canonical output will not use negative or None starts or ends. So I've updated the test cases below:
shape = (4, 10, 12, 7)
first = (slice(None), 3, slice(3, 9))
second = (2, slice(1, 3), slice(5))
expected_combined = (2, 3, slice(4, 6, 1), slice(0, 5, 1))
assert compose(shape, first, second) == expected_combined
other_first = (slice(2, 4), slice(None), slice(2, 7))
other_second = (0, 3, slice(2, -1), slice(5))
assert compose(shape, other_first, other_second) == expected_combined

Related

Z3 optimize by index not a value

With greate respect to the answer of #alias there: (Find minimum sum) I would like to solve similar puzzle. Having 4 agents and 4 type of works. Each agent does work on some price (see initial matrix in the code). I need find the optimal allocation of agents to the particular work. Following code almost copy paste from the mentioned answer:
initial = ( # Row - agent, Column - work
(7, 7, 3, 6),
(4, 9, 5, 4),
(5, 5, 4, 5),
(6, 4, 7, 2)
)
opt = Optimize()
agent = [Int(f"a_{i}") for i, _ in enumerate(initial)]
opt.add(And(*(a != b for a, b in itertools.combinations(agent, 2))))
for w, row in zip(agent, initial):
opt.add(Or(*[w == val for val in row]))
minTotal = Int("minTotal")
opt.add(minTotal == sum(agent))
opt.minimize(minTotal)
print(opt.check())
print(opt.model())
Mathematically correct answer: [a_2 = 4, a_1 = 5, a_3 = 2, a_0 = 3, minTotal = 14] is not working for me, because I need get index of agent instead.
Now, my question - how to rework the code to optimize by indexes instead of values? I've tried to leverage the Array but have no idea how to minimize multiple sums.
You can simply keep track of the indexes and walk each row to pick the corresponding element. Note that the itertools.combinations can be replaced by Distinct. We also add extra checks to make sure the indices are between 1 and 4 to ensure there's no out-of-bounds access:
from z3 import *
initial = ( # Row - agent, Column - work
(7, 7, 3, 6),
(4, 9, 5, 4),
(5, 5, 4, 5),
(6, 4, 7, 2)
)
opt = Optimize()
def choose(i, vs):
if vs:
return If(i == 1, vs[0], choose(i-1, vs[1:]))
else:
return 0
agent = [Int(f"a_{i}") for i, _ in enumerate(initial)]
opt.add(Distinct(*agent))
for a, row in zip(agent, initial):
opt.add(a >= 1)
opt.add(a <= 4)
opt.add(Or(*[choose(a, row) == val for val in row]))
minTotal = Int("minTotal")
opt.add(minTotal == sum(choose(a, row) for a, row in zip (agent, initial)))
opt.minimize(minTotal)
print(opt.check())
print(opt.model())
This prints:
sat
[a_1 = 1, a_0 = 3, a_2 = 2, a_3 = 4, minTotal = 14]
which I believe is what you're looking for.
Note that z3 also supports arrays, which you can use for this problem. However, in SMTLib, arrays are not "bounded" like in programming languages. They're indexed by all elements of their domain type. So, you won't get much of a benefit from doing that, and the above formulation seems to be the most straightforward.

Numpy: Efficient way to convert indices of a square matrix to its upper triangular indices [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
Question: given a tuple of index, return its order in upper triangular indices. Here is an example:
Suppose we have a square matrix A of shape (3, 3).
A has 6 upper triangular indices, namely, (0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2).
Now I know an element at index (1, 2), which is a index belongs to the upper triangular part of A. I would like to return 4 (which means it is the 5th element in all upper triangular indices.)
Any ideas on how to do that in general?
Best,
Zhihao
One can write down the explicit formula:
def utr_idx(N, i, j):
return (2*N+1-i)*i//2 + j-i
Demo:
>>> N = 127
>>> X = np.transpose(np.triu_indices(N))
>>> utr_idx(N, *X[2123])
2123
For an n×n matrix, the (i, j)-th item of the upper triangle is the i×(2×n-i+1)/2+j-i-th element of the matrix.
We can also do the math in reverse and obtain the (i, j) element for the k-th element with:
i = ⌊(-√((2n+1)2-8k)+2n+1)/2⌋ and j = k+i-i×(2×n-i+1)/2
So for example:
from math import floor, sqrt
def coor_to_idx(n, i, j):
return i*(2*n-i+1)//2+j-i
def idx_to_coor(n, k):
i = floor((-sqrt((2*n+1)*(2*n+1)-8*k)+2*n+1)/2)
j = k + i - i*(2*n-i+1)//2
return i, j
For example:
>>> [idx_to_coor(4, i) for i in range(10)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]
>>> [coor_to_idx(4, i, j) for i in range(4) for j in range(i, 4)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Given the numbers are not huge (well if these are huge, calculations are no longer done in constant time), we can thus calculate the k-th coordinate in O(1), for example:
>>> idx_to_coor(1234567, 123456789)
(100, 5139)
which is equivalent to obtaining it through enumeration:
>>> next(islice(((i, j) for i in range(1234567) for j in range(i, 1234567)), 123456789, None))
(100, 5139)
Here converting indices to a coordinate can also have, for large numbers, some rounding errors due to floating point imprecision.
IIUC, you can get the indexes using itertools combinations with replacement
>>> ind = tuple(itertools.combinations_with_replacement(range(3),2))
((0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2))
To retrieve the index, just use index method
>>> ind.index((1,2))
4
You could use np.triu_indices and a dictionary:
import numpy as np
iu1 = np.triu_indices(3)
table = {(i, j): c for c, (i, j) in enumerate(zip(*iu1))}
print(table[(1, 2)])
Output
4
Similar to #DanielMesejo, you can use np.triu_indices with either argwhere or nonzero:
my_index = (1,2)
>>> np.nonzero((np.stack(np.triu_indices(3), axis=1) == my_index).all(1))
(array([4]),)
>>> np.argwhere((np.stack(np.triu_indices(3), axis=1) == my_index).all(1))
array([[4]])
Explanation:
np.stack(np.triu_indices(3), axis=1) gives you the indices of your upper triangle in order:
array([[0, 0],
[0, 1],
[0, 2],
[1, 1],
[1, 2],
[2, 2]])
So all you have to do is find where it matches [1,2] (which you can do with the == operator and all)
Constructing upper indices would be costly. We can directly get the corresponding index like so -
def triu_index(N, x, y):
# Get index corresponding to (x,y) in upper triangular list
idx = np.r_[0,np.arange(N,1,-1).cumsum()]
return idx[x]+y-x
Sample run -
In [271]: triu_index(N=3, x=1, y=2)
Out[271]: 4

create list of adjacent elements of another list in Python

I am looking to take as input a list and then create another list which contains tuples (or sub-lists) of adjacent elements from the original list, wrapping around for the beginning and ending elements. The input/output would look like this:
l_in = [0, 1, 2, 3]
l_out = [(3, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 0)]
My question is closely related to another titled getting successive adjacent elements of a list, but this other question does not take into account wrapping around for the end elements and only handles pairs of elements rather than triplets.
I have a somewhat longer approach to do this involving rotating deques and zipping them together:
from collections import deque
l_in = [0, 1, 2, 3]
deq = deque(l_in)
deq.rotate(1)
deq_prev = deque(deq)
deq.rotate(-2)
deq_next = deque(deq)
deq.rotate(1)
l_out = list(zip(deq_prev, deq, deq_next))
# l_out is [(3, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 0)]
However, I feel like there is probably a more elegant (and/or efficient) way to do this using other built-in Python functionality. If, for instance, the rotate() function of deque returned the rotated list instead of modifying it in place, this could be a one- or two-liner (though this approach of zipping together rotated lists is perhaps not the most efficient). How can I accomplish this more elegantly and/or efficiently?
One approach may be to use itertools combined with more_itertools.windowed:
import itertools as it
import more_itertools as mit
l_in = [0, 1, 2, 3]
n = len(l_in)
list(it.islice(mit.windowed(it.cycle(l_in), 3), n-1, 2*n-1))
# [(3, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 0)]
Here we generated an infinite cycle of sliding windows and sliced the desired subset.
FWIW, here is an abstraction of the latter code for a general, flexible solution given any iterable input e.g. range(5), "abcde", iter([0, 1, 2, 3]), etc.:
def get_windows(iterable, size=3, offset=-1):
"""Return an iterable of windows including an optional offset."""
it1, it2 = it.tee(iterable)
n = mit.ilen(it1)
return it.islice(mit.windowed(it.cycle(it2), size), n+offset, 2*n+offset)
list(get_windows(l_in))
# [(3, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 0)]
list(get_windows("abc", size=2))
# [('c', 'a'), ('a', 'b'), ('b', 'c')]
list(get_windows(range(5), size=2, offset=-2))
# [(3, 4), (4, 0), (0, 1), (1, 2), (2, 3)]
Note: more-itertools is a separate library, easily installed via:
> pip install more_itertools
This can be done with slices:
l_in = [0, 1, 2, 3]
l_in = [l_in[-1]] + l_in + [l_in[0]]
l_out = [l_in[i:i+3] for i in range(len(l_in)-2)]
Well, or such a perversion:
div = len(l_in)
n = 3
l_out = [l_in[i % div: i % div + 3]
if len(l_in[i % div: i % div + 3]) == 3
else l_in[i % div: i % div + 3] + l_in[:3 - len(l_in[i % div: i % div + 3])]
for i in range(3, len(l_in) + 3 * n + 2)]
You can specify the number of iterations.
Well I figured out a better solution as I was writing the question, but I already went through the work of writing it, so here goes. This solution is at least much more concise:
l_out = list(zip(l_in[-1:] + l_in[:-1], l_in, l_in[1:] + l_in[:1]))
See this post for different answers on how to rotate lists in Python.
The one-line solution above should be at least as efficient as the solution in the question (based on my understanding) since the slicing should not be more expensive than the rotating and copying of the deques (see https://wiki.python.org/moin/TimeComplexity).
Other answers with more efficient (or elegant) solutions are still welcome though.
as you found there is a list rotation slicing based idiom lst[i:] + lst[:i]
using it inside a comprehension taking a variable n for the number of adjacent elements wanted is more general [lst[i:] + lst[:i] for i in range(n)]
so everything can be parameterized, the number of adjacent elements n in the cyclic rotation and the 'phase' p, the starting point if not the 'natural' 0 base index, although the default p=-1 is set to -1 to fit the apparant desired output
tst = list(range(4))
def rot(lst, n, p=-1):
return list(zip(*([lst[i+p:] + lst[:i+p] for i in range(n)])))
rot(tst, 3)
Out[2]: [(3, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 0)]
showing the shortend code as per the comment

Algorithm to generate subsets satisfying binary relation

I am looking for a reasonable algorithm in python (well, because I have rather complicated mathematical objects implemented in python, so I cannot change the language) to achieve the following:
I am given a reflexive, symmetric binary relation bin_rel on a set X. The requested function maximal_compatible_subsets(X, bin_rel) should return all containmentwise maximal subsets of X such that the binary relation holds for all pairs a,b of elements in X.
In some more detail: Suppose I am given a binary relation on a set of objects, say
def bin_rel(elt1,elt2):
# return True if elt1 and elt2 satisfy the relation and False otherwise
# Example: set intersection, in this case, elt1 and elt2 are sets
# and the relation picks those pairs that have a nonempty intersection
return elt1.intersection(elt2)
I can also assume that the relation bin_rel is reflexive (this is, binary_rel(a,a) is True holds) and symmetric (this is, binary_rel(a,b) is binary_rel(b,a) holds).
I am now given a set X and a function bin_rel as above and seek an efficient algorithm to obtain the desired subsets of X
For example, in the case of the set intersection above (with sets replaced by lists for easier reading):
> X = [ [1,2,3], [1,3], [1,6], [3,4], [3,5], [4,5] ]
> maximal_compatible_subsets(X,bin_rel)
[[[1,2,3],[1,3],[1,6]], [[1,2,3],[1,3],[3,4],[3,5]], [[3,4],[3,5],[4,5]]]
This problem doesn't seem to be very exotic, so most welcome would be a pointer to an efficient existing snippet of code.
As Matt Timmermans noted this is finding maximal cliques problem that can be solved by Bron–Kerbosch algorithm. NetworkX has implementation that can be used for Python.
If you want to use python straight out of the box, you could use the following as a starting point:
from itertools import combinations
def maximal_compatible_subsets(X, bin_rel):
retval = []
for i in range(len(X) + 1, 1, -1):
for j in combinations(X, i):
if all(bin_rel(a, b) for a, b in combinations(j, 2)) and not any(set(j).issubset(a) for a in retval):
retval.append(tuple(j))
return tuple(retval)
if __name__ == '__main__':
x = ( (1,2,3), (1,3), (1,6), (3,4), (3,5), (4,5) )
def nonempty_intersection(a, b):
return set(a).intersection(b)
print x
print maximal_compatible_subsets(x, nonempty_intersection)
Outputs:
((1, 2, 3), (1, 3), (1, 6), (3, 4), (3, 5), (4, 5))
(((1, 2, 3), (1, 3), (3, 4), (3, 5)), ((1, 2, 3), (1, 3), (1, 6)), ((3, 4), (3, 5), (4, 5)))

Generating all possible combinations of a list, "itertools.combinations" misses some results

Given a list of items in Python, how can I get all the possible combinations of the items?
There are several similar questions on this site, that suggest using itertools.combinations, but that returns only a subset of what I need:
stuff = [1, 2, 3]
for L in range(0, len(stuff)+1):
for subset in itertools.combinations(stuff, L):
print(subset)
()
(1,)
(2,)
(3,)
(1, 2)
(1, 3)
(2, 3)
(1, 2, 3)
As you see, it returns only items in a strict order, not returning (2, 1), (3, 2), (3, 1), (2, 1, 3), (3, 1, 2), (2, 3, 1), and (3, 2, 1). Is there some workaround for that? I can't seem to come up with anything.
Use itertools.permutations:
>>> import itertools
>>> stuff = [1, 2, 3]
>>> for L in range(0, len(stuff)+1):
for subset in itertools.permutations(stuff, L):
print(subset)
...
()
(1,)
(2,)
(3,)
(1, 2)
(1, 3)
(2, 1)
(2, 3)
(3, 1)
....
Help on itertools.permutations:
permutations(iterable[, r]) --> permutations object
Return successive r-length permutations of elements in the iterable.
permutations(range(3), 2) --> (0,1), (0,2), (1,0), (1,2), (2,0), (2,1)
You can generate all the combinations of a list in python using this simple code
import itertools
a = [1,2,3,4]
for i in xrange(1,len(a)+1):
print list(itertools.combinations(a,i))
Result:
[(1,), (2,), (3,), (4,)]
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
[(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]
[(1, 2, 3, 4)]
Are you looking for itertools.permutations instead?
From help(itertools.permutations),
Help on class permutations in module itertools:
class permutations(__builtin__.object)
| permutations(iterable[, r]) --> permutations object
|
| Return successive r-length permutations of elements in the iterable.
|
| permutations(range(3), 2) --> (0,1), (0,2), (1,0), (1,2), (2,0), (2,1)
Sample Code :
>>> from itertools import permutations
>>> stuff = [1, 2, 3]
>>> for i in range(0, len(stuff)+1):
for subset in permutations(stuff, i):
print(subset)
()
(1,)
(2,)
(3,)
(1, 2)
(1, 3)
(2, 1)
(2, 3)
(3, 1)
(3, 2)
(1, 2, 3)
(1, 3, 2)
(2, 1, 3)
(2, 3, 1)
(3, 1, 2)
(3, 2, 1)
From Wikipedia, the difference between permutations and combinations :
Permutation :
Informally, a permutation of a set of objects is an arrangement of those objects into a particular order. For example, there are six permutations of the set {1,2,3}, namely (1,2,3), (1,3,2), (2,1,3), (2,3,1), (3,1,2), and (3,2,1).
Combination :
In mathematics a combination is a way of selecting several things out of a larger group, where (unlike permutations) order does not matter.
itertools.permutations is going to be what you want. By mathematical definition, order does not matter for combinations, meaning (1,2) is considered identical to (2,1). Whereas with permutations, each distinct ordering counts as a unique permutation, so (1,2) and (2,1) are completely different.
Here is a solution without itertools
First lets define a translation between an indicator vector of 0 and 1s and a sub-list (1 if the item is in the sublist)
def indicators2sublist(indicators,arr):
return [item for item,indicator in zip(arr,indicators) if int(indicator)==1]
Next, Well define a mapping from a number between 0 and 2^n-1 to the its binary vector representation (using string's format function) :
def bin(n,sz):
return ('{d:0'+str(sz)+'b}').format(d=n)
All we have left to do, is to iterate all the possible numbers, and call indicators2sublist
def all_sublists(arr):
sz=len(arr)
for n in xrange(0,2**sz):
b=bin(n,sz)
yield indicators2sublist(b,arr)
I assume you want all possible combinations as 'sets' of values. Here is a piece of code that I wrote that might help give you an idea:
def getAllCombinations(object_list):
uniq_objs = set(object_list)
combinations = []
for obj in uniq_objs:
for i in range(0,len(combinations)):
combinations.append(combinations[i].union([obj]))
combinations.append(set([obj]))
return combinations
Here is a sample:
combinations = getAllCombinations([20,10,30])
combinations.sort(key = lambda s: len(s))
print combinations
... [set([10]), set([20]), set([30]), set([10, 20]), set([10, 30]), set([20, 30]), set([10, 20, 30])]
I think this has n! time complexity, so be careful. This works but may not be most efficient
just thought i'd put this out there since i couldn't fine EVERY possible outcome and keeping in mind i only have the rawest most basic of knowledge when it comes to python and there's probably a much more elegant solution...(also excuse the poor variable names
testing = [1, 2, 3]
testing2= [0]
n = -1
def testingSomethingElse(number):
try:
testing2[0:len(testing2)] == testing[0]
n = -1
testing2[number] += 1
except IndexError:
testing2.append(testing[0])
while True:
n += 1
testing2[0] = testing[n]
print(testing2)
if testing2[0] == testing[-1]:
try:
n = -1
testing2[1] += 1
except IndexError:
testing2.append(testing[0])
for i in range(len(testing2)):
if testing2[i] == 4:
testingSomethingElse(i+1)
testing2[i] = testing[0]
i got away with == 4 because i'm working with integers but you may have to modify that accordingly...

Categories

Resources