1

Given a value for phi, theta, n_1, and n_2, I need to find all possible pairs (N_1, N_2) that meet the following criteria:

0 <= N_1 <= n_1
0 <= N_2 <= n_2
N_1 - phi * N_2 >= theta

What is the most efficient way to do this in Python? Obviously I could use two for loops -- iterating over all possible values for N_1 and N_2 (from the first two criteria), and saving only those pairs that meet the last criterion -- but this would be fairly inefficient.

abcd
  • 10,215
  • 15
  • 51
  • 85
  • Firstly, you could move `(-phi * N_2)` in the last inequality from the left part to the right: `N_1 >= theta + phi * N_2`, and this defines the lower bound of the `N_1`. Secondly - are `phi`, `theta`, `n_1`, `N_1`, `n_2`, `N_2` integers? – awesoon Sep 02 '15 at 03:11
  • @soon (1) yep. (2) all the `n`s and `N`s are non-negative integers. `phi` and `theta` are not necessarily integers. `phi` must be positive. looks like `theta` is generally non-negative. – abcd Sep 02 '15 at 03:12

2 Answers2

1

You could use numpy and vectorization, something like it below

import numpy as np

phi = 0.5
theta = 1
n1 = 10
n2 = 20

N1 = np.random.randint(-100, 100, size=100)
N2 = np.random.randint(-100, 100, size=100)

N1 = N1[(N1 >= 0) & (N1 <= n1)]
N2 = N2[(N2 >= 0) & (N2 <= n2)]

a = N2 * theta + phi
res = N1.reshape(N1.shape[0], 1) - a.reshape(1, a.shape[0])

indices = np.argwhere(res >= 0)
pairs = zip(N1[indices[:,0]], N2[indices[:,1]])

example output of pairs

[(8, 3),
 (8, 6),
 (8, 5),
 (8, 1),
 (3, 1),
 (9, 3),
 (9, 8),
 (9, 8),
 (9, 6),
 (9, 5),
 (9, 6),
 (9, 6),
 (9, 5),
 (9, 8),
 (9, 1)]

per @dbliss request, here is the modualized version and its test

import numpy as np


def calc_combination(N1, N2, n1, n2, theta, phi):
    N1 = N1[(N1 >= 0) & (N1 <= n1)]
    N2 = N2[(N2 >= 0) & (N2 <= n2)]

    a = N2 * theta + phi
    res = N1.reshape(N1.shape[0], 1) - a.reshape(1, a.shape[0])

    indices = np.argwhere(res >= 0)
    pairs = zip(N1[indices[:,0]], N2[indices[:,1]])
    return pairs


def test_case():
    n1 = 5
    n2 = 1
    theta = 2
    phi = 2

    N1 = np.arange(n1 + 1)
    N2 = np.arange(n2 + 1)

    assert (calc_combination(N1, N2, n1, n2, theta, phi) ==
            [(2, 0), (3, 0), (4, 0), (4, 1), (5, 0), (5, 1)])

test_case()
zyxue
  • 7,904
  • 5
  • 48
  • 74
0

This works:

import itertools

def _get_N_1_and_N_2(n_1, n_2, phi, theta):
    """Get the (N_1, N_2) pairs as defined in Griffith (1963).

    See Equation 3.

    Parameters
    ----------
    n_1 : integer
      Number of excitatory inputs.

    n_2 : integer
      Number of inhibitory inputs.

    phi : number
      Factor that captures the difference between excitatory and
      inhibitory synaptic efficiencies.

    theta : number
      Spike threshold.

    """
    N_1 = range(n_1 + 1)
    N_2 = range(n_2 + 1)
    possible_N_1_N_2 = itertools.product(N_1, N_2)
    N_1_N_2 = []
    for N_1, N_2 in possible_N_1_N_2:
        if N_1 - phi * N_2 >= theta:
            N_1_N_2.append((N_1, N_2))
    return N_1_N_2

I guess I could make that for loop with the if statement a really messy list comprehension. But . . . naa.

Here's the test:

import nose.tools as nt

def test__get_N_1_and_N_2():

    # Figure 3A in Griffith, 1963, Biophy J.
    n_1 = 4
    n_2 = 0
    theta = 2
    phi = 1
    desired = [(2, 0), (3, 0), (4, 0)]
    actual = _get_N_1_and_N_2(n_1, n_2, phi, theta)
    nt.assert_list_equal(desired, actual)

    # Figure 3B.
    n_1 = 5
    n_2 = 1
    theta = 2
    phi = 2
    desired = [(2, 0), (3, 0), (4, 0), (4, 1), (5, 0), (5, 1)]
    actual = _get_N_1_and_N_2(n_1, n_2, phi, theta)
    nt.assert_list_equal(desired, actual)
abcd
  • 10,215
  • 15
  • 51
  • 85