Sympy simplify sum of Kronecker delta - sum

How does one simplify sums of Kronecker delta expressions in sympy?
For example consider Sum(KroneckerDelta(i,j),(i,0,n-1)) or Sum(KroneckerDelta(i, j, (0, n - 1)), (i, 0, n - 1)):
from sympy import *
from sympy.concrete.delta import _simplify_delta
n = symbols('n')
j = tensor.Idx('j')
i = tensor.Idx('i')
_simplify_delta(simplify(Sum(KroneckerDelta(i,j),(i,0,n-1))))
_simplify_delta(simplify(Sum(KroneckerDelta(i,j,(0,n-1)),(i,0,n-1))))
Outputs Sum(KroneckerDelta(i, j), (i, 0, n - 1)) and Sum(KroneckerDelta(i, j, (0, n - 1)), (i, 0, n - 1))
If j is constrained to be between 0 and n-1 (how do I tell the sympy that?), then this should reduce to 1, which occurs at i==j. Moreover if the sum is more complicated, I would expect it to remove the sum and replace the sum variable i with the variable j.
Moreover, I would be interested in a resource for all sorts of simplifications in sympy for KroneckerDelta functions. I recently found out how to perform implicit matrix differentiation in sympy and KroneckerDelta functions appear everywhere.
Edit: I found a solution, kind of. Its not automated.
I found more functions inside of sympy.concrete.delta using help(sympy.concrete.delta). If we copy the resulting expression and replace Sum with sympy.concrete.delta.deltasummation then the desired simplification happens. I am still curious if there is a delta simplification package that tries all these things automatically.

You can evaluate summations using Sum().doit() or summation:
In [1]: from sympy import *
...: from sympy.concrete.delta import _simplify_delta
...:
...: n = symbols("n")
...: j = tensor.Idx("j")
...: i = tensor.Idx("i")
In [2]: s = Sum(KroneckerDelta(i, j), (i, 0, n - 1))
In [3]: s
Out[3]:
n - 1
___
╲
╲ δ
╱ i,j
╱
‾‾‾
i = 0
In [4]: s.doit()
Out[4]:
⎧1 for j ≥ 0 ∧ j ≤ n - 1
⎨
⎩0 otherwise
In [5]: summation(KroneckerDelta(i, j), (i, 0, n - 1))
Out[5]:
⎧1 for j ≥ 0 ∧ j ≤ n - 1
⎨
⎩0 otherwise
There isn't currently a way to specify in assumptions that j<=n-1 although you can use j = symbols('j', nonnegative=True) to specify that j>=0. You can also manually replace those conditions with true though e.g.:
In [8]: s.doit().subs({j >= 0: True, j <= n-1: True})
Out[8]: 1
The second summation where you give bounds for the KroneckerDelta will compute automatically:
In [11]: s2 = Sum(KroneckerDelta(i, j, (0, n - 1)), (i, 0, n - 1))
In [12]: s2
Out[12]:
n - 1
___
╲
╲ δ
╱ i,j
╱
‾‾‾
i = 0
In [13]: s2.doit()
Out[13]: 1

Related

How to create a graph of 2n nodes on a circle w/ each node is connected to all others except the opposite node + f(n) on either side of the opposite

I would like to create a graph that puts 2n points spaced uniformly on a unit circle, and connects each node to all other nodes except the opposite node, as well as k nodes on either side of the opposite node, where k is a function of n.
I am using the NetworkX package to create the graph.
I know how to manually specify the number of nodes to exclude on either side of the opposite node, but I don't know how to use a function of n instead of doing it manually. I'm sure it is quite simple, but how can this be done?
For example, the code below will exclude the opposite node as well as 5 nodes on either side of the opposite node.
import networkx as nx
import numpy as np
import matplotlib.pyplot as plt
def create_circle_graph(n):
G = nx.Graph()
theta = np.linspace(0, 2*np.pi, 2*n, endpoint=False)
for i in range(2*n):
G.add_node(i, pos=np.array([np.cos(theta[i]), np.sin(theta[i])]))
for j in range(i+1, 2*n):
if (i + n) % (2 * n) != j and (i + n + 1) % (2 * n) != j and (i + n - 1) % (2 * n) != j and (i + n + 2) % (2 * n) != j and (i + n - 2) % (2 * n) != j and (i + n + 3) % (2 * n) != j and (i + n - 3) % (2 * n) != j and (i + n + 4) % (2 * n) != j and (i + n - 4) % (2 * n) != j and (i + n + 5) % (2 * n) != j and (i + n - 5) % (2 * n) != j:
G.add_edge(i, j)
return G
n = 10
G = create_circle_graph(n)
pos = nx.get_node_attributes(G, 'pos')
nx.draw(G, pos, with_labels=True)
plt.show()
The main difficulty is to find the appropriate if statement while creating the edges. Once you figure that part out the rest is pretty straightforward since you can use nx.circular_layout(G) to automatically position your nodes.
Below is a possible approach for this task:
import networkx as nx
import numpy as np
import matplotlib.pyplot as plt
def create_circle_graph(n,k): #2*n the number and k the radius of the neighborhood
G = nx.Graph()
[G.add_node(i) for i in range(2*n)] # create nodes
for i in range(2*n):
for j in range(2*n):
if i<j and (j-i<n-k or j-i>n+k): #appropriate if statement for this task
G.add_edge(i,j)
return G
And here is an example of the code with n=5 and varying values for k:
n=5
fig,axs=plt.subplots(1,5,figsize=(15,10))
for k,ax in enumerate(fig.axes): #loop through k and subplots
G=create_circle_graph(n,k)
posG=nx.circular_layout(G) #set up positions for the nodes
ax.set_title('k='+str(k))
nx.draw(G,pos=posG,with_labels=True,ax=ax)
ax.set_aspect('equal')
plt.tight_layout()
plt.show()

Efficient way to calculate 3D matrix multiplication using numpy

How can I efficiently write and calculate this multiplication using numpy:
for k in range(K):
for i in range(SIZE):
for j in range(SIZE):
for i_b in range(B_SIZE):
for j_b in range(B_SIZE):
for k_b in range(k+1):
data[k, i * w + i_b, j * h + j_b] += arr1[k_b, i_b, j_b] * arr2[k_b, i, j]
For example:
SIZE, B_SIZE = 32, 8
arr1.shape -> (8, 8, 8)
arr2.shape -> (8, 32, 32)
data.shape -> (K, 256, 256)
Thank you.
You can use Numba for such kind of non-trivial case and rework the loops to use efficiently the CPU cache. Here is an example:
import numba as nb
#nb.njit
def compute(data, arr1, arr2):
for k in range(K):
for k_b in range(k+1):
for i in range(SIZE):
for j in range(SIZE):
tmp = arr2[k_b, i, j]
for i_b in range(B_SIZE):
for j_b in range(B_SIZE):
data[k, i * w + i_b, j * h + j_b] += arr1[k_b, i_b, j_b] * tmp
If you do this operation once, then you can pre-compile the Numba code by providing the types of the arrays. If K is big, then you can parallelize the code using #nb.njit(parallel=True) and use for k in nb.prange(K) rather than for k in range(K). This should be several order of magnitude fater.

How to generate Im, Re octave filter

I have function, which convert result from FFT to octave band (or 1/n - octave):
Function OctaveFilter(LowFreq, HighFreq, Im, Re) 'For amplitude
Dim i, j, SortedData(), F_From(), F_To()
Redim SortedData(Bins * n), F_From(Bins * n), F_To(Bins * n)
Dim p
For i = 1 To Bins * n
F_To(i) = Int(HighFreq(i) / df)
F_From(i) = Int(LowFreq(i) / df)
if (F_From(i) = 0) Then F_From(i) = 1 ' We cannot start from index 0
Next
For i = 1 To Bins * n
SortedData(i) = 0
For j = F_From(i) To F_To(i)
If (Length >= j) Then
SortedData(i) = SortedData(i) + Im(j)^2 + Re(j)^2
End If
Next
Next
SortInBins = sqrt(SortedData)
End Function
For example this FFT:
Amplitude
converts to this 1/3- octave bands:
1/3 - octave
But from FFT I also have Re and Im part. I want to convert these parts to octave band too. Is it the same function? How can I convert this Im part imaginary part to similar result (1/3 - octave) ?

Multiplying multidimensional array in python

I have two arrays:
L, M, N = 6, 31, 500
A = np.random.random((L, M, N))
B = np.random.random((L, L))
I am trying to get an array C such that:
C = B * A
C has dimension [L, M, N]
I tried answer posted at this link but it hasn't given me the desired output.
A for loop version of above code is:
L, M, N = 6, 31, 500
A = np.random.random((L, M, N))
B = np.random.random((L, L))
z1 = []
for j in range(M):
a = np.squeeze(A[:, j, :])
z1.append(np.dot(B, a))
z2 = np.stack(z1)
I think you are looking for numpy.tensordot() where you can specify along which axes to sum:
np.tensordot(B,A,axes=(1,0))

How to express c[i,j,k] = a[i,j] * b[i,k] in Numpy/Theano?

The definition
c[i,j,k] = a[i,j] * b[i,k] for any i, j, k
is an element-wise product with respect to i, and an outer product with respect to j and k. Is there any way to express this in NumPy/Theano without loops?
I found a solution that works with both Numpy and Theano:
c = a[:, :, np.newaxis] * b[:, np.newaxis, :]