-8

I want to make this piece of Python code (loops) just a single line, but I don’t know how. My goal in this project is to prove that not always the smaller the code (in number of lines) the better, because its readability gets worse.

KE, ndof, K, F, U = KT, 2 * (nely + 1) * (nelx + 1), lil_matrix((2 * (nely + 1) * (nelx + 1), 2 * (nely + 1) * (nelx + 1))), zeros(2 * (nely + 1) * (nelx + 1)), zeros(2 * (nely + 1) * (nelx + 1))
    for ely in range(1,nely+1):
        for elx in range(1,nelx+1):
            K[np.ix_([2*((nely+1)*(elx-1)+ely)-2, 2*((nely+1)*(elx-1)+ely)-1, 2*((nely+1)*elx+ely)-2, 2*((nely+1)*elx+ely)-1, 2*((nely+1)*elx+ely), 2*((nely+1)*elx+ely)+1, 2*((nely+1)*(elx-1)+ely), 2*((nely+1)*(elx-1)+ely)+1],[2*((nely+1)*(elx-1)+ely)-2, 2*((nely+1)*(elx-1)+ely)-1, 2*((nely+1)*elx+ely)-2, 2*((nely+1)*elx+ely)-1, 2*((nely+1)*elx+ely), 2*((nely+1)*elx+ely)+1, 2*((nely+1)*(elx-1)+ely), 2*((nely+1)*(elx-1)+ely)+1])] += x[ely-1,elx-1]**penal * KE

And the same thing in this case:

    dcn = zeros((nely, nelx))
    for i in range(nelx):
        for j in range(nely):
            dcn[j, i] = (dcn[j, i] + sum((rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5) * (rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5 > 0) * x[l, k] * dc[l, k] for k in range(int(max(i - round(rmin), 0)), int(min(i + round(rmin) + 1, nelx))) for l in range(int(max(j - round(rmin), 0)), int(min(j + round(rmin) + 1, nely))))) / (x[j, i] * ((sum((rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5) * (rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5 > 0) for k in range(int(max(i - round(rmin), 0)), int(min(i + round(rmin) + 1, nelx))) for l in range(int(max(j - round(rmin), 0)), int(min(j + round(rmin) + 1, nely)))))+((sum((rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5) * (rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5 > 0) for k in range(int(max(i - round(rmin), 0)), int(min(i + round(rmin) + 1, nelx))) for l in range(int(max(j - round(rmin), 0)), int(min(j + round(rmin) + 1, nely))))) == 0)))

I tried using list comprehension and np.sum, but it didn’t work. Also, I need to make a function containing those loops in a single line too:

def check(nelx, nely, rmin, x, dc):
    dcn = zeros((nely, nelx))
    for i in range(nelx):
        for j in range(nely):
            dcn[j, i] = (dcn[j, i] + sum((rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5) * (rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5 > 0) * x[l, k] * dc[l, k] for k in range(int(max(i - round(rmin), 0)), int(min(i + round(rmin) + 1, nelx))) for l in range(int(max(j - round(rmin), 0)), int(min(j + round(rmin) + 1, nely))))) / (x[j, i] * ((sum((rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5) * (rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5 > 0) for k in range(int(max(i - round(rmin), 0)), int(min(i + round(rmin) + 1, nelx))) for l in range(int(max(j - round(rmin), 0)), int(min(j + round(rmin) + 1, nely)))))+((sum((rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5) * (rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5 > 0) for k in range(int(max(i - round(rmin), 0)), int(min(i + round(rmin) + 1, nelx))) for l in range(int(max(j - round(rmin), 0)), int(min(j + round(rmin) + 1, nely))))) == 0)))
    return dcn
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 4
    Instead of looking for list comprehension, look for writing readable code. Having a statement with 800+ characters is just a pain to debug once you haven't looked at this code for a month or so. Bad practice. – trincot Aug 13 '23 at 20:14
  • 3
    This should be made into more lines, not less. It's practically unreadable. – interjay Aug 13 '23 at 20:16
  • 1
    A list comprehension is good for a loop that repeatedly `appends` a value to a list. It returns a new list. It does not modify an existing one, or print/display intermediate results. – hpaulj Aug 13 '23 at 20:17
  • this code and your possible list comprehension will NOT pass the review stage, how much faster do you expect the refactored code be, it is now already unreadable, anybody who has to work on your code will ditch it and write something readable, because most likely it has bugs – rioV8 Aug 13 '23 at 20:18
  • 3
    "I need to make a function containing those loops in a single line too" no, you **never** need to do that. List comprehensions aren't "single line for loops". They are mapping/filtering constructs that create a `list`. They mostly exist as *syntactic sugar*, they are not for performance optimization. They are for making your code more *readable*. If they don't make your code more readable, then don't use them. – juanpa.arrivillaga Aug 13 '23 at 20:23
  • 2
    I had to scroll way to the end of the `K` line to find the `+=`. The `ix_` indexing for `K` is unreadable. Also I see that ``K` is produced by `lil_matrix`. What's that? I'm guessing it;s from `scipy.sparse`, but that needs to be clear. Similarly the `zeros` functions are better written as `np.zeros`. Again making the source clear. – hpaulj Aug 13 '23 at 20:30
  • If you just want to get rid of the nested loops you can use `for ely, elx in itertools.product(range(1, nely+1), range(1, nelx+1)):`. Other than that, I agree with the rest of the comments. – Barmar Aug 13 '23 at 22:10
  • My goal in this project is to prove that not always the smaller the code (in number of lines) the better, because its readability gets worse. So, once I write this code in a single line, that will be my prove. – Guilherme Salmi Aug 14 '23 at 11:46
  • There is also a lot of repeated code. For instance, `rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5` is repeated six times. And aren't there built-in functions for some of it, e.g., [Euclidean distance](https://en.wiktionary.org/wiki/Euclidean_distance#Noun)? – Peter Mortensen Aug 15 '23 at 09:40

1 Answers1

1

Of course you can do that:

def check2(nelx,nely,rmin,x,dc):return np.array([[((sum((rmin -((i-k)**2+(j-l)**2)**0.5)*(rmin -((i-k)**2+(j-l)**2)**0.5>0)*x[l,k]*dc[l,k] for k in range(int(max(i-round(rmin),0)),int(min(i+round(rmin)+1,nelx))) for l in range(int(max(j-round(rmin),0)),int(min(j+round(rmin)+1,nely)))))/(x[j,i]*((sum((rmin-((i-k)**2+(j-l)**2)**0.5)*(rmin-((i-k)**2+(j-l)**2)**0.5>0) for k in range(int(max(i-round(rmin),0)),int(min(i+round(rmin)+1,nelx))) for l in range(int(max(j-round(rmin),0)),int(min(j+round(rmin)+1,nely)))))+((sum((rmin-((i-k)**2+(j-l)**2)**0.5)*(rmin-((i-k)**2+(j-l)**2)**0.5>0) for k in range(int(max(i-round(rmin),0)),int(min(i+round(rmin)+1,nelx))) for l in range(int(max(j-round(rmin),0)),int(min(j+round(rmin)+1,nely)))))==0)))) for i in range(nelx)] for j in range(nely)])

You can also try to use as many lines as possible:

def check3(
    nelx,
    nely,
    rmin,
    x,
    dc
):
    dcn = zeros(
        (
            nely,
            nelx
        )
    )
    for i in range(
        nelx
    ):
        for j in range(
            nely
        ):
            dcn[
                j,
                i
            ] = (
                dcn[
                    j,
                    i
                ] + sum(
                    (
                        rmin - (
                            (
                                i - k
                            ) ** 2 + (
                                j - l
                            ) ** 2
                        ) ** 0.5
                    ) * (
                        rmin - (
                            (
                                i - k
                            ) ** 2 + (
                                j - l
                            ) ** 2
                        ) ** 0.5 > 0
                    ) * x[
                        l,
                        k
                    ] * dc[
                        l,
                        k
                    ] for k in range(
                        int(
                            max(
                                i - round(
                                    rmin
                                ),
                                0
                            )
                        ),
                        int(
                            min(
                                i + round(
                                    rmin
                                ) + 1,
                                nelx
                            )
                        )
                    ) for l in range(
                        int(
                            max(
                                j - round(
                                    rmin
                                ),
                                0
                            )
                        ),
                        int(
                            min(
                                j + round(
                                    rmin
                                ) + 1,
                                nely
                            )
                        )
                    )
                )
            ) / (
                x[
                    j,
                    i
                ] * (
                    (
                        sum(
                            (
                                rmin - (
                                    (
                                        i - k
                                    ) ** 2 + (
                                        j - l
                                    ) ** 2
                                ) ** 0.5
                            ) * (
                                rmin - (
                                    (
                                        i - k
                                    ) ** 2 + (
                                        j - l
                                    ) ** 2
                                ) ** 0.5 > 0
                            ) for k in range(
                                int(
                                    max(
                                        i - round(
                                            rmin
                                        ),
                                        0
                                    )
                                ), int(
                                    min(
                                        i + round(
                                            rmin
                                        ) + 1,
                                        nelx
                                    )
                                )
                            ) for l in range(
                                int(
                                    max(
                                        j - round(
                                            rmin
                                        ),
                                        0
                                    )
                                ), int(
                                    min(
                                        j + round(
                                            rmin
                                        ) + 1,
                                        nely
                                    )
                                )
                            )
                        )
                    ) + (
                        (
                            sum(
                                (
                                    rmin - (
                                        (
                                            i - k
                                        ) ** 2 + (
                                            j - l
                                        ) ** 2
                                    ) ** 0.5
                                ) * (
                                    rmin - (
                                        (
                                            i - k
                                        ) ** 2 + (
                                            j - l
                                        ) ** 2
                                    ) ** 0.5 > 0
                                ) for k in range(
                                    int(
                                        max(
                                            i - round(
                                                rmin
                                            ), 0
                                        )
                                    ), int(
                                        min(
                                            i + round(
                                                rmin
                                            ) + 1,
                                            nelx
                                        )
                                    )
                                ) for l in range(
                                    int(
                                        max(
                                            j - round(
                                                rmin
                                            ),
                                            0
                                        )
                                    ), int(
                                        min(
                                            j + round(
                                                rmin
                                            ) + 1,
                                            nely
                                        )
                                    )
                                )
                            )
                        ) == 0
                    )
                )
            )
    return dcn

However, if you care moreso about code readability and with that maintainability, and potentially also runtime efficiency (e.g. in case you can use variables to avoid computing the same thing more than once) as opposed to minimizing or maximizing the number of lines of your code, then you are probably better off with something in between, as others have already pointed out in the comments ;)

import numpy as np
zeros = np.zeros

def check(nelx, nely, rmin, x, dc):
    dcn = zeros((nely, nelx))
    for i in range(nelx):
        for j in range(nely):
            dcn[j, i] = (dcn[j, i] + sum((rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5) * (rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5 > 0) * x[l, k] * dc[l, k] for k in range(int(max(i - round(rmin), 0)), int(min(i + round(rmin) + 1, nelx))) for l in range(int(max(j - round(rmin), 0)), int(min(j + round(rmin) + 1, nely))))) / (x[j, i] * ((sum((rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5) * (rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5 > 0) for k in range(int(max(i - round(rmin), 0)), int(min(i + round(rmin) + 1, nelx))) for l in range(int(max(j - round(rmin), 0)), int(min(j + round(rmin) + 1, nely)))))+((sum((rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5) * (rmin - ((i - k) ** 2 + (j - l) ** 2) ** 0.5 > 0) for k in range(int(max(i - round(rmin), 0)), int(min(i + round(rmin) + 1, nelx))) for l in range(int(max(j - round(rmin), 0)), int(min(j + round(rmin) + 1, nely))))) == 0)))
    return dcn

for nelx in range(1, 5):
    for nely in range(1, 5):
        for rmin in range(1, 5):
            for k in range(10):
                a, b, c, d = np.random.randint(1, 10, 4)
                x = np.random.randint(1, 10, (a, b))
                dc = np.random.randint(1, 10, (c, d))
                passes = False
                try:
                    res = check(nelx, nely, rmin, x, dc)
                    assert np.isfinite(res).all()
                    passes = True
                except:
                    pass
                if passes:
                    assert np.array_equal(res, check2(nelx, nely, rmin, x, dc))
                    assert np.array_equal(res, check3(nelx, nely, rmin, x, dc))
Michael Hodel
  • 2,845
  • 1
  • 5
  • 10