Exemple #1
0
def fitness(moveablePts):
    global nutrient_values, product_values

    try:
        xPoints = moveablePts[0::2]  #get x points np array view from flat list
        yPoints = moveablePts[1::2]  #get y points np array view from flat list
    except:
        xPoints = moveablePts._value[0::2]
        yPoints = moveablePts._value[1::2]
    try:
        xIntPoints = list([int(x) for x in xPoints])
        yIntPoints = list([int(y) for y in yPoints])
    except:
        xIntPoints = list([int(x) for x in xPoints._value])
        yIntPoints = list([int(y) for y in yPoints._value])

    nutrient_values = np.zeros((HowManyCells, HowManyCells))
    product_values = np.zeros((HowManyCells, HowManyCells))

    nutrient_values = doPDE(nutrient_values, moveablePts, xPoints, yPoints,
                            xIntPoints, yIntPoints)
    product_values = doPDE(product_values, moveablePts, xPoints, yPoints,
                           xIntPoints, yIntPoints)

    odeResult = doODE(nutrient_values, product_values, moveablePts, xPoints,
                      yPoints, xIntPoints, yIntPoints)
    nutrient_values += odeResult[0]
    product_values += odeResult[1]
    # nutrient_values.clip(min=0)  #put all negative values to 0
    # product_values.clip(min=0)
    return (nutrient_values[10][10])
Exemple #2
0
    def generate_edges(self, tri, pts):
        edges = []
        for _, s in enumerate(tri.simplices):
            tri_pts = [pts[i] for i in list(s)]

            for i, pt in enumerate(tri_pts):
                if i == 0:
                    continue
                elif pt == tri_pts[-1]:  # Wrap around
                    self.add_edge_to_graph(pt, tri_pts[0])

                self.add_edge_to_graph(tri_pts[i - 1], pt)

        topl_to_topR = ([self.min_range,
                         self.max_range], [self.max_range, self.max_range])
        btml_to_btmR = ([self.min_range,
                         self.min_range], [self.max_range, self.min_range])

        for pt_pair in [topl_to_topR, btml_to_btmR]:
            pt1, pt2 = pt_pair

            if pt2 in self.graph[tuple(pt1)]:
                idx = self.graph[tuple(pt1)].index(pt2)
                del self.graph[tuple(pt1)][idx]
            elif pt1 in self.graph[tuple(pt2)]:
                idx = self.graph[tuple(pt2)].index(pt1)
                del self.graph[tuple(pt1)][idx]

        for tuple_pt, lst_pt_list in self.graph.items():
            for lst_pt in lst_pt_list:
                # edges.append((np.asarray(tuple_pt), list(np_pt)))
                edges.append((list(tuple_pt), lst_pt))

        return edges
Exemple #3
0
def doPDE(values, movablePts, xPoints, yPoints, xIntPoints, yIntPoints):
    # Update the values based on diffusion of the proteins to nearby cells
    D = 0.1  # diffusion parameter
    valuesT = np.transpose(values)
    adjustmentPDEX = D * nonLinearAdjustment(xPoints)
    adjustmentPDEY = D * nonLinearAdjustment(yPoints)

    #simple diffusion is just a convolution
    convolveLinear = np.array([1 * D, -2 * D, 1 * D])
    # accumulate the changes due to diffusion
    for rep in range(50):
        # print(rep)
        newValuesX = list([])
        newValuesY = list([])
        for i in range(HowManyCells):
            row = values[i] + sig.convolve(
                values[i], convolveLinear)[1:-1]  #take off first and last
            rowY = valuesT[i] + sig.convolve(
                valuesT[i], convolveLinear)[1:-1]  #take off first and last
            # non-linear diffusion, add the adjustment
            if i in xIntPoints:
                row = row + np.multiply(row, adjustmentPDEX)
            if i in yIntPoints:
                rowY = rowY + np.multiply(rowY, adjustmentPDEY)
            newValuesX.append(row)
            newValuesY.append(rowY)

        #Merge rows and transposed columns
        values = np.array(newValuesX) + np.array(newValuesY).T
        # add source at each iteration
        values = values + addSources3(xPoints, yPoints)
        #Update transposed values
        valuesT = values.T
    # the total update returned is the difference between the original values and the values after diffusion
    return values
Exemple #4
0
 def plot(self, *args, start=None, end=None, step=500, **kwargs):
     if start == None or end == None:
         xs = np.linspace(self.end, self.start, step)
         ys = list(map(self.function, xs))
     else:
         xs = np.linspace(start, end, step)
         ys = list(map(self.function, xs))
     plt.plot(xs, ys, *args, **kwargs)
Exemple #5
0
def nonLinearAdjustment(movablePts):
    # adds an adjustment to the material transfer to take into account
    # the actural position of the cell point in space
    # adjustment is constant for each simulation, because the points do
    # not move so compute once
    allAdjustment = np.zeros(HowManyCells)
    for x in list(movablePts):  #only single numbers in x one D
        try:
            pointI = int(x)
        except:
            pointI = int(x._value)
        thisAdj = []
        totalAdj = 0  # accumulate the changes around the center point
        for xI in range(0, HowManyCells):
            # find the array locations just before or just after the moveable point
            if ((pointI == xI - 1 and pointI > 0)
                    or (pointI == xI + 1 and pointI < HowManyCells)):
                deltaConc = distToConc(
                    abs(x - (xI + 0.5)))  #distance off from the center
                thisAdj.append(deltaConc)
                totalAdj = totalAdj + deltaConc  #accun
            # Otherwise no adjustment
            else:
                thisAdj.append(0)
        #accumulate this movable point into the total adjustment
        allAdjustment = allAdjustment + np.array(thisAdj)
    return allAdjustment
Exemple #6
0
def get_hand_seq(Theta, bones_lengths, fingers_angles):
    hand_seq = list([[]] * 5)
    for finger_ix in range(5):
        hand_seq[finger_ix] = get_finger_bone_seq(finger_ix, Theta,
                                                  bones_lengths,
                                                  fingers_angles)
    return hand_seq
Exemple #7
0
    def update_hillclimb_pts(self, new_mvable_pts):
        self.graph = dict()
        pts_lst = []
        prev = None
        cur = None

        for i in range(0, len(list(new_mvable_pts))):

            cur = list(new_mvable_pts)[i]
            # print('updating points', cur)
            x = float(cur[0])
            y = float(cur[1])
            if x < self.min_range + 1:
                x = float(self.min_range + 1)
            elif x > self.max_range - 1:
                x = float(self.max_range - 1)

            if y < self.min_range + 1:
                y = float(self.min_range + 1)
            elif y > self.max_range - 1:
                y = float(self.max_range - 1)

            cur = [x, y]
            pts_lst = pts_lst + [cur]

        self.moveable_pts = pts_lst
        self.remove_dup_pts()

        pts = []
        if self.has_side_nodes:
            pts = self.moveable_pts + self.left_wall + self.right_wall + self.left_wall_startend + self.right_wall_startend
        else:
            pts = self.moveable_pts + self.left_wall_startend + self.right_wall_startend

        self.pts = sorted(pts, key=lambda x: (-x[0], x[1]), reverse=True)

        self.tri = Delaunay(self.pts)
        self.edges = self.generate_edges(self.tri, self.pts)

        self.img = None
        self.img = self.convert_to_img(self.edges, self.max_range)

        self.update_count = self.update_count + 1
Exemple #8
0
    def add_flows_to_img(self, flow_dict):
        # print('In add_flows_to_img')
        img = []
        self.pts_on_vessels = defaultdict(lambda: 0.0)
        for _ in range(int(self.max_range + 1)):
            img.append([0.0 for i in range(int(self.max_range + 1))])

        for pt_key, flow_val in flow_dict.items():
            pt1, pt2 = pt_key
            pt1 = [int(i) for i in list(pt1)]
            pt2 = [int(i) for i in list(pt2)]
            pts_on_line = list(bresenham(pt1[0], pt1[1], pt2[0], pt2[1]))
            for x, y in pts_on_line:
                img[x][y] = flow_val
                self.pts_on_vessels[(x, y)] = flow_val

        self.img = img
        self.Q = np.array(img)

        return self.img
Exemple #9
0
def rotate_diff_axis(axis, vec, ix_start, theta, eps=1e-6):
    if abs(theta) <= eps:
        return vec
    if axis == 0:
        x, y, z = rotate_diff_x(vec, ix_start, theta)
    elif axis == 1:
        x, y, z = rotate_diff_y(vec, ix_start, theta)
    elif axis == 2:
        x, y, z = rotate_diff_z(vec, ix_start, theta)
    else:
        return None
    return list([x, y, z])
Exemple #10
0
def get_finger_bone_seq_no_rot(finger_ix, bones_lengths):
    finger_bone_angles_ixs = [0] * 4
    for i in range(4):
        finger_bone_angles_ixs[i] = i + 3
    finger_bone_seq = list([[0., 0., 0.]] * 4)
    curr_seq_len = 0.
    for i in range(4):
        finger_bone_seq[i] = [
            curr_seq_len + bones_lengths[finger_ix][i], 0., 0.
        ]
        curr_seq_len += bones_lengths[finger_ix][0]
    return finger_bone_seq
Exemple #11
0
def doODE(nutrient_values, product_values, movablePts, xPoints, yPoints,
          xIntPoints, yIntPoints):
    k = 0.01
    product_result = list([])
    nutrient_result = list([])
    for iy in range(HowManyCells):
        product_result_row = list([])
        nutrient_result_row = list([])
        for ix in range(HowManyCells):
            isVessel = False
            if ix == xIntPoints[0] and iy == yIntPoints[0]:
                isVessel = True
            x = product_values[ix][iy]
            n = nutrient_values[ix][iy]
            dx = deltaProduct(x, n, k, isVessel)
            dn = deltaNutrient(x, n, k, isVessel)
            product_result_row.append(dx)
            nutrient_result_row.append(dn)
        product_result.append(product_result_row)
        nutrient_result.append(nutrient_result_row)

    return (np.array(product_result), np.array(nutrient_result))
Exemple #12
0
def get_bones_lengths():
    '''

    :return skeleton model fixed bone lengths in mm
    '''
    bone_lengths = list([[]] * 5)
    # finger
    bone_lengths[0] = [52., 43., 35., 32.]
    # index
    bone_lengths[1] = [86., 42., 34., 29.]
    # middle
    bone_lengths[2] = [78., 48., 34., 28.]
    # ring
    bone_lengths[3] = [77., 50., 32., 29.]
    # little1
    bone_lengths[4] = [77., 29., 21., 23.]
    return bone_lengths
Exemple #13
0
def get_fingers_angles_canonical(right_hand=True):
    '''

    :return: bone angles of hand canonical pose
    '''
    finger_angles = list([[0., 0., 0.]] * 5)
    if right_hand:
        # finger
        finger_angles[0] = [0., 0.2, 0.785]
        # index
        finger_angles[1] = [0., 0., 0.3925]
        # middle
        finger_angles[2] = [0., 0., 0.]
        # ring
        finger_angles[3] = [0., 0., 5.8875]
        # little
        finger_angles[4] = [0., 0., 5.495]
    return finger_angles
Exemple #14
0
def get_finger_bone_seq(finger_ix, Theta, bones_lengths, fingers_angles):
    # get local bone sequence positions, without rotation
    finger_bone_seq = list([[0., 0., 0.]] * 4)
    for i in range(4):
        finger_bone_seq[i] = [bones_lengths[finger_ix][i], 0., 0.]
    # get finger-dependent indexes of Theta and axes of rotation
    theta_ixs, axes_rot = get_finger_theta_ixs_and_axes(finger_ix)
    # rotate each finger bone with Theta
    for i in range(4):
        ix_rev = 3 - i
        angle = Theta[theta_ixs[ix_rev]]
        ax_rot = axes_rot[ix_rev]
        finger_bone_seq[ix_rev] = rotate_diff_axis(ax_rot,
                                                   finger_bone_seq[ix_rev], 0,
                                                   angle)
        # update "children" bones
        j = ix_rev
        while j < 3:
            finger_bone_seq[j + 1] = rotate_diff_axis(ax_rot,
                                                      finger_bone_seq[j + 1],
                                                      0, angle)
            j += 1
    # put all finger bones in absolute position to hand root
    for i in range(3):
        finger_bone_seq[i + 1] = [
            finger_bone_seq[i + 1][0] + finger_bone_seq[i][0],
            finger_bone_seq[i + 1][1] + finger_bone_seq[i][1],
            finger_bone_seq[i + 1][2] + finger_bone_seq[i][2]
        ]
    # rotate each finger with its finger canonical angle for each axis
    for i in range(4):
        for ax in range(3):
            angle = fingers_angles[finger_ix][ax]
            finger_bone_seq[i] = rotate_diff_axis(ax, finger_bone_seq[i], 0,
                                                  angle)
    # rotate each finger according to the hand root rotation
    for i in range(4):
        for j in range(3):
            finger_bone_seq[i] = rotate_diff_axis(j, finger_bone_seq[i], 0,
                                                  Theta[j])
    return finger_bone_seq
Exemple #15
0
    def convert_to_img(self, edges, max_range):
        img = []
        for _ in range(int(max_range + 1.0)):
            img.append([0.0 for i in range(int(max_range + 1.0))])

        for edge in edges:
            pt1, pt2 = edge
            pt1 = [int(i) for i in pt1]
            pt2 = [int(i) for i in pt2]
            # TODO(zcase): Account for it points go <min or > max for both x and y
            # print(pt1)
            # print(pt2)
            pts_on_line = list(bresenham(pt1[0], pt1[1], pt2[0], pt2[1]))
            # print(pts_on_line)
            for x, y in pts_on_line:
                if (x == pt1[0] and y == pt1[1]) or (x == pt2[0]
                                                     and y == pt2[1]):
                    img[x][y] = 2
                else:
                    img[x][y] = 1

        return img
Exemple #16
0
def nonlinearDiffusion(mvble_pts, img, D, linearConv):
    #http://greg-ashton.physics.monash.edu/applying-python-functions-in-moving-windows.html
    #https://stackoverflow.com/questions/12816293/vectorize-this-convolution-type-loop-more-efficiently-in-numpy
    h, w = np.array(img).shape
    deltaDomain2 = []
    for _ in range(w):
        deltaDomain2.append([0.0 for _ in range(h)])
    # maximum distance from moveable point to center of neighboring grid location
    #maxD = math.sqrt(0.5 * 0.5 + 1.5*1.5)
    for i in range(len(mvble_pts)):
        pt = mvble_pts[i]
        x = pt[0]
        y = pt[1]
        int_x = 0
        int_y = 0

        # print(mvble_pts[i][0], type(mvble_pts[i][0]))
        # get the int coordinates of this moveable point
        if type(x) != type(np.array((1, 1))) and type(x) != type(1) and (
                type(mvble_pts[i][0]) != float
                and type(mvble_pts[i][0]) != np.float64):
            # if type(x) != type(np.array((1,1))) and type(mvble_pts[i][0]) != np.float64:
            int_x = int(np.array(mvble_pts[i][0]._value))
            int_y = int(np.array(mvble_pts[i][1]._value))
        else:
            int_x = int(np.array(mvble_pts[i][0]))
            int_y = int(np.array(mvble_pts[i][1]))
        # int_x = int(np.array(mvble_pts[i][0]._value))
        # int_y = int(np.array(mvble_pts[i][1]._value))
        np_pt = np.array([x, y])

        inc = 0.5  # to center of neighbor grid locations
        # compute all the distances with the neighbors
        dist_0 = np.linalg.norm(np_pt -
                                np.array([int_x - 1 + inc, int_y - 1 + inc]))
        dist_1 = np.linalg.norm(np_pt -
                                np.array([int_x + inc, int_y - 1 + inc]))
        dist_2 = np.linalg.norm(np_pt -
                                np.array([int_x + 1 + inc, int_y - 1 + inc]))

        dist_3 = np.linalg.norm(np_pt -
                                np.array([int_x - 1 + inc, int_y + inc]))
        dist_4 = np.linalg.norm(np_pt - np.array([int_x + inc, int_y + inc]))
        dist_5 = np.linalg.norm(np_pt -
                                np.array([int_x + 1 + inc, int_y + inc]))

        dist_6 = np.linalg.norm(np_pt -
                                np.array([int_x - 1 + inc, int_y + 1 + inc]))
        dist_7 = np.linalg.norm(np_pt -
                                np.array([int_x + inc, int_y + 1 + inc]))
        dist_8 = np.linalg.norm(np_pt -
                                np.array([int_x + 1 + inc, int_y + 1 + inc]))
        # calculate non-linear concentrations adjustments needed based on the position of the point at that grid location
        # contribution of concentration to neighbor adjusts the contribution already calculated by linear diffution
        # let d be the distance from the moveable point to the center of the neighboring grid location
        # then adjustment in concentration is 1 - d.
        # if d=1, no adjustment (realdy done this)
        # if d<1, then closer so positive adjustment
        # if d>1, then farther away so negative adjustment
        # let this function be distToConc(d) --> conc
        # center = sum([distToConc(d) for d in [dist_0, dist_1, dist_2, dist_3, dist_4, dist_5, dist_6, dist_7, dist_8]]) * -1
        # convolution = [[distToConc(dist_0), distToConc(dist_1), distToConc(dist_2)],
        #                [distToConc(dist_3), center, distToConc(dist_5)],
        #                [distToConc(dist_6), distToConc(dist_7), distToConc(dist_8)]]
        center = sum([
            d for d in [
                dist_0, dist_1, dist_2, dist_3, dist_4, dist_5, dist_6, dist_7,
                dist_8
            ]
        ]) * -1
        convolution = [[dist_0, dist_1, dist_2], [dist_3, center, dist_5],
                       [dist_6, dist_7, dist_8]]

        convolution = np.array(linearConv) - np.array(convolution)
        convolution = [val for val in list(convolution)]

        # center = sum([distToConc(d) for d in [dist_0, dist_1, dist_2, dist_3, dist_4, dist_5, dist_6, dist_7, dist_8]]) * -1
        # convolution = [[distToConc(dist_0), distToConc(dist_1), distToConc(dist_2)],
        #                [distToConc(dist_3), center, distToConc(dist_5)],
        #                [distToConc(dist_6), distToConc(dist_7), distToConc(dist_8)]]

        print('\nNonLinear Conv (After Subtracting from Linear)')
        print(np.array(convolution))
        print('Sqrt(2): ', np.sqrt(2), 1 - np.sqrt(2))
        print('D0: ', dist_0)  #, distToConc(dist_0))
        print('D1: ', dist_1)  #, distToConc(dist_1))
        print('D2: ', dist_2)  #, distToConc(dist_2))
        print('D3: ', dist_3)  #, distToConc(dist_3))
        print(
            'D4: ',
            sum([
                d for d in [
                    dist_0, dist_1, dist_2, dist_3, dist_4, dist_5, dist_6,
                    dist_7, dist_8
                ]
            ]) * -1
        )  #, sum([distToConc(d) for d in [dist_0, dist_1, dist_2, dist_3, dist_4, dist_5, dist_6, dist_7, dist_8]]) * -1)
        print('D5: ', dist_5)  #, distToConc(dist_5))
        print('D6: ', dist_6)  #, distToConc(dist_6))
        print('D7: ', dist_7)  #, distToConc(dist_7))
        print('D8: ', dist_8)  #, distToConc(dist_8))
        # try:
        deltaDomain2 = get_submatrix_add(deltaDomain2, (int_x, int_y),
                                         convolution)
        # print(np.array(deltaDomain2))
        # print((int_x, int_y))
        # os.sys.exit()
        # except Exception as e:
        #     print(e)
        #     print(np.array(deltaDomain2), np.array(deltaDomain2).shape)
        #     print((int_x, int_y))
        #     print(np.array(convolution))
    # print(np.array(deltaDomain2) * D)
    # return np.array(deltaDomain2) * D
    return np.array(deltaDomain2)
Exemple #17
0
def get_hand_seq_canonical(bones_lengths, fingers_angles):
    hand_seq = list([[]] * 5)
    for finger_ix in range(5):
        hand_seq[finger_ix] = get_finger_canonical_bone_seq(
            finger_ix, bones_lengths, fingers_angles)
    return hand_seq
Exemple #18
0
        ax_values.set_ylabel('value')
        ax_values.imshow(nutrient_values)

        ax_product.cla()
        ax_product.set_title('Product')
        ax_product.set_xlabel('position')
        ax_product.set_ylabel('value')
        ax_product.imshow(product_values)

        plt.draw()
        saveFigureImage(iteration)
        plt.pause(0.001)
        return 3

    gradPDE = grad(fitness)
    mvable_pts = list([12.4, 14.99, 5.3, 6.8
                       ])  #flat list for autograd but points are (x,y) (x,y)
    if useAdam:
        m = np.zeros(np.array(mvable_pts).shape, dtype=np.float64)
        v = np.zeros(np.array(mvable_pts).shape, dtype=np.float64)
        b1 = 0.9
        b2 = 0.999
        eps = 10**-8
    # print(gradPDE((25.99, 3.4)))

    for i in range(500):
        grad_pts = gradPDE(mvable_pts)
        print(grad_pts)
        if useAdam:
            m = (1 - b1) * np.array(
                grad_pts, dtype=np.float64) + b1 * m  # First  moment estimate.
            v = (1 - b2) * (np.array(grad_pts, dtype=np.float64)**
Exemple #19
0
    def update_moveable_pts(self, new_mvable_pts):
        # print('In update_moveable_pts')
        # print('VasGen2 236: ', new_mvable_pts)
        # print(type(new_mvable_pts))
        self.graph = dict()
        pts_lst = []
        prev = None
        cur = None
        # print('\n IN UPDATE PTS')
        # print('new_mv_pts: ', new_mvable_pts)
        # for i in range(2, len(list(new_mvable_pts)._value)+2, 2):
        for i in range(2, len(list(new_mvable_pts)) + 2, 2):
            # cur  = list(new_mvable_pts)._value[i-2:i]
            cur = list(new_mvable_pts)[i - 2:i]
            x = float(cur[0])
            y = float(cur[1])
            if x < self.min_range + 1:
                x = float(self.min_range + 1)
            elif x > self.max_range - 1:
                x = float(self.max_range - 1)

            if y < self.min_range + 1:
                y = float(self.min_range + 1)
            elif y > self.max_range - 1:
                y = float(self.max_range - 1)

            cur = [x, y]
            # print('First Cur: ', cur)

            # if i > 2:
            #     prev = [float(i) for i in prev]
            #     cur = [float(i) for i in cur]
            #     print('prev: ', prev)
            #     print('cur : ', cur)
            #     pts_lst = pts_lst + [prev, cur]
            # prev = cur
            pts_lst = pts_lst + [cur]

        # print('\nhere: ')
        # print(pts_lst, type(pts_lst), len(pts_lst))
        # os.sys.exit()
        # print('hERE: ', list(new_mvable_pts)._value)
        self.moveable_pts = pts_lst
        self.remove_dup_pts()

        # print('HERE 2:         ', self.moveable_pts)

        pts = []
        if self.has_side_nodes:
            pts = self.moveable_pts + self.left_wall + self.right_wall + self.left_wall_startend + self.right_wall_startend
        else:
            pts = self.moveable_pts + self.left_wall_startend + self.right_wall_startend
        # print('VasGen2: ', np.array(pts), type(pts))
        self.pts = sorted(pts, key=lambda x: (-x[0], x[1]), reverse=True)

        self.tri = Delaunay(self.pts)
        self.edges = self.generate_edges(self.tri, self.pts)

        self.img = None
        self.img = self.convert_to_img(self.edges, self.max_range)

        self.update_count = self.update_count + 1
def eval_repar(e, thts, env={}):
    """
    Summary: computes the reparameterization term in our estimator.
    Args:
      - e     : Expr
      - thts  : float array
      - env   : (str -> (func * float)) dict
    Returns:
      - ret   : func
      - retvl : float
      - epss  : float array
      - xs    : float array
      - logpq : func
    where
      - env[var_str] = return value of Var(var_str) as (function of \THT, float)
      - ret(\THT) = return value of e (as a function of \THT)
      - retvl  = ret(thts)
      - epss   = values sampled from N(0,1)
      - xs     = T_thts(epss)
      - logpq(\THT) = log p(X,Y) - log q_\THT(X) |_{X=T_\THT(epss)}
    here capital math symbols denote vectors.
    """

    if isinstance(e, Cnst):
        ret = lambda _thts, c=e.c: c
        retvl = ret([])
        epss = anp.array([])
        xs = anp.array([])
        logpq = lambda _thts: 0.0

    elif isinstance(e, Var):
        assert (e.v in env)
        (ret, retvl) = env[e.v]
        epss = anp.array([])
        xs = anp.array([])
        logpq = lambda _thts: 0.0

    elif isinstance(e, Linear):
        ret = None  # ASSUME: (Linear ...) appear only in the conditional part of If.
        retvl = e.c0 + sum([ci * env[vi][1] for (ci, vi) in e.cv_l])
        epss = anp.array([])
        xs = anp.array([])
        logpq = lambda _thts: 0.0

    elif isinstance(e, App):
        # recursive calls
        num_args = len(e.args)
        (ret_sub, retvl_sub, epss_sub, xs_sub, logpq_sub)\
            = zip(*[ eval_repar(e.args[i], thts, env) for i in range(num_args) ])

        # compute: all but ret, retvl
        epss = anp.concatenate(epss_sub)
        xs = anp.concatenate(xs_sub)
        logpq = merge_logpqs(logpq_sub)

        # compute: ret, retvl
        op = App.OP_DICT[num_args][e.op]
        ret   = lambda _thts, op=op, ret_sub=ret_sub, num_args=num_args:\
                op(*[  ret_sub[i](_thts) for i in range(num_args)])
        retvl = op(*[retvl_sub[i] for i in range(num_args)])

    elif isinstance(e, If):
        # recursive calls
        (_, retvl_1, epss_1, xs_1, logpq_1)\
            =  eval_repar(e.e1, thts, env)
        (ret_r, retvl_r, epss_r, xs_r, logpq_r)\
            = (eval_repar(e.e2, thts, env) if retvl_1 > 0 else\
               eval_repar(e.e3, thts, env))

        # compute: all
        ret = ret_r
        retvl = retvl_r
        epss = anp.concatenate((epss_1, epss_r))
        xs = anp.concatenate((xs_1, xs_r))
        logpq = merge_logpqs([logpq_1, logpq_r])

    elif isinstance(e, Let):
        # recursive calls
        (ret_1, retvl_1, epss_1, xs_1, logpq_1) = eval_repar(e.e1, thts, env)
        env_new = util.copy_add_dict(env, {e.v1.v: (ret_1, retvl_1)})
        (ret_2, retvl_2, epss_2, xs_2,
         logpq_2) = eval_repar(e.e2, thts, env_new)

        # compute: all
        ret = ret_2
        retvl = retvl_2
        epss = anp.concatenate((epss_1, epss_2))
        xs = anp.concatenate((xs_1, xs_2))
        logpq = merge_logpqs([logpq_1, logpq_2])

    elif isinstance(e, Sample):
        # recursive calls
        (ret_1, retvl_1, epss_1, xs_1, logpq_1) = eval_repar(e.e1, thts, env)
        (ret_2, retvl_2, epss_2, xs_2, logpq_2) = eval_repar(e.e2, thts, env)

        # compute: all but logpq
        stind = e.stind['thts']
        eps_3 = np.random.normal(0, 1)  # do sampling
        eps2x_cur = lambda _tht, eps=eps_3: _tht[0] + util.softplus_anp(_tht[1]
                                                                        ) * eps
        eps2x_3 = lambda _thts, eps2x_cur=eps2x_cur, stind=stind: eps2x_cur(
            _thts[stind:stind + 2])
        x_3 = eps2x_3(thts)

        ret = lambda _thts, eps2x_3=eps2x_3: eps2x_3(_thts)
        retvl = x_3  # use current thts value to compute return value
        epss = anp.concatenate((epss_1, epss_2, anp.array([eps_3])))
        xs = anp.concatenate((xs_1, xs_2, anp.array([x_3])))

        # compute: logpq
        def logpq_3(_thts, ret=ret, ret_1=ret_1, ret_2=ret_2, stind=stind):
            # compute: log p(x|p_loc,p_scale) - log q(x|q_loc,q_scale)
            x = ret(_thts)
            p_loc = ret_1(_thts)
            p_scale = ret_2(_thts)
            q_loc = _thts[stind]
            q_scale = util.softplus_anp(_thts[stind + 1])
            return (ascipy.stats.norm.logpdf(x, p_loc, p_scale) -\
                    ascipy.stats.norm.logpdf(x, q_loc, q_scale))

        logpq = merge_logpqs([logpq_1, logpq_2, logpq_3])

    elif isinstance(e, Fsample):
        # recursive calls
        (ret_1, retvl_1, epss_1, xs_1, logpq_1) = eval_repar(e.e1, thts, env)
        (ret_2, retvl_2, epss_2, xs_2, logpq_2) = eval_repar(e.e2, thts, env)

        # compute: all
        x_3 = np.random.normal(retvl_1, retvl_2)  # do sampling
        ret = lambda _thts, x_3=x_3: x_3
        retvl = x_3
        epss = anp.concatenate((epss_1, epss_2))
        xs = anp.concatenate((xs_1, xs_2))
        logpq = merge_logpqs([logpq_1, logpq_2])

    elif isinstance(e, Observe):
        # recursive calls
        num_args = len(e.args)
        (ret_sub, retvl_sub, epss_sub, xs_sub, logpq_sub)\
            = zip(*[ eval_repar(e.args[i], thts, env) for i in range(num_args) ])

        # compute: all but logpq
        ret = lambda _thts, c=e.c1.c: c
        retvl = ret([])
        epss = anp.concatenate(epss_sub)
        xs = anp.concatenate(xs_sub)

        # compute: logpq
        dstr_logpdf = Observe.DSTR_DICT[e.dstr]

        def logpq_cur(_thts,
                      dstr_logpdf=dstr_logpdf,
                      c=e.c1.c,
                      ret_sub=ret_sub,
                      num_args=num_args):
            # compute: log p(c|p_loc,p_scale)
            return dstr_logpdf(c,
                               *[ret_sub[i](_thts) for i in range(num_args)])

        logpq = merge_logpqs(list(logpq_sub) + [logpq_cur])

    else:
        assert (False)
    return (ret, retvl, epss, xs, logpq)