1

I made a Python simulator that runs on the basis of user-provided arguments. To use the program, I run multiple random simulations (controlled with a seed value). I use GNU parallel to run the simulator with arguments in a similar manner as shown below:

parallel 'run-sim --seed {1} --power {2}' ::: <seed args> ::: <power args>

Now, there is a third argument --num that I want to use, but want to link that argument with the seed value. So that, for every seed value only one num value is used. However, the same num argument should not be used with every power value.

In a nutshell, this table should make you understand better:

| Power      | Seed        | num          |
|:-----------|------------:|:------------:|
| 10         |      0      |     100      |
| 10         |      1      |     150      |
| 10         |      2      |     200      |
| 10         |      3      |     250      |
|:-----------|------------:|:------------:|
| 20         |      0      |     300      |
| 20         |      1      |     350      |
| 20         |      2      |     400      |
| 20         |      3      |     450      |
....

(The table format may not be suitable for mobile devices)

If I were to write the above implementation using a for loop, I would do something like:

for p in power:
   for s, n in (seed, num[p])
       simulate(p, s, n)

Where power is a 1D array, seed is a 1D array and num is a 2D array where a row depicts the corresponding num values for a power p.

My Solution:

Use multiple parallel statements for each power value, and use the --link parameter of parallel to bind the seed and num arguments.

parallel --link 'run-sim --seed {1} --num {2} --power 10' ::: 0 1 2 3 ::: 100 150 200 250
parallel --link 'run-sim --seed {1} --num {2} --power 20' ::: 0 1 2 3 ::: 300 350 400 450
...

The problem with this solution would be that I would have to limit the number of jobs for each statement based upon the number of power values. My computer can handle 50 extra processes before going into cardiac arrest, therefore for 3 power values, I would have to limit the jobs for each statement to 12.

What I am looking for

A single liner such that I don't have to run multiple parallel statements and fix the number of jobs to 50.

2 Answers 2

2

It is unclear how you determine num. You can use an array based on power and seed:

$ declare -A num=([10,0]=100 [10,1]=150 [10,2]=200 [10,3]=250 [20,0]=300 [20,1]=350 [20,2]=400 [20,3]=450 [30,0]=133 [30,1]=166 [30,2]=200 [30,3]=233)
$ env_parallel -j 50 echo power {1} seed {2} num '${num[{1},{2}]}' ::: 10 20 30 ::: 0 1 2 3

Or an array based on the sequence number:

$ num=(dummy 100 150 200 250 300 350 400 450 133 166 200 233)
$ env_parallel -j 50 echo power {1} seed {2} num '${num[{#}]}' ::: 10 20 30 ::: 0 1 2 3

Or maybe:

parallel -j 50 echo power {1} seed '{=1 $_=(seq()-1)%4=}' num {2} ::: 10 10 10 10 20 20 20 20 30 30 30 30 :::+ 100 150 200 250 300 350 400 450 133 166 200 233
2
  • The second answer makes more sense, where I can flatten out the 2D num array (as I have described in the question) and use it as you have done in part 2.
    – MaJoR21
    Apr 9, 2020 at 21:38
  • Now I feel like I could have done something similar as you had done in your previous answer. Sorry for the trouble.
    – MaJoR21
    Apr 9, 2020 at 21:39
1

For part of the answer you want this, correct?

$ parallel --link -k echo {1} {2} ::: {0..3} ::: {100..450..50}
0 100
1 150
2 200
3 250
0 300
1 350
2 400
3 450

If so, one way to do what I think you want would be

$ parallel -k echo {1} {2} ::: {10..20..10} ::: "$(parallel --link -k echo {1} {2} ::: {0..3} ::: {100..450..50})"
10 0 100
10 1 150
10 2 200
10 3 250
10 0 300
10 1 350
10 2 400
10 3 450
20 0 100
20 1 150
20 2 200
20 3 250
20 0 300
20 1 350
20 2 400
20 3 450

Another way would be (with a sort thrown in to show it in the order you want; it wouldn't be necessary in the actual run):

$ parallel --link -k echo {1} {2} ::: {0..3} ::: {100..450..50} | parallel -a- echo {2} {1} ::: {10..20..10} | sort -k 1,1 -k3,3 -k2,2
10 0 100
10 1 150
10 2 200
10 3 250
10 0 300
10 1 350
10 2 400
10 3 450
20 0 100
20 1 150
20 2 200
20 3 250
20 0 300
20 1 350
20 2 400
20 3 450

Yet another way would be to have parallel invoke parallel:

$ parallel parallel --link --arg-sep ,,, echo {1} ,,, {0..3} ,,, {100..450..50} ::: {10..20..10}
10 0 100
10 1 150
10 2 200
10 3 250
10 0 300
10 1 350
10 2 400
10 3 450
20 0 100
20 1 150
20 2 200
20 3 250
20 0 300
20 1 350
20 2 400
20 3 450

This works because the “inner” parallel uses commas instead of colons for argument separators, so the “outer” parallel doesn't “see” the linked argument.

While I was working on a way to make that more understandable (there's an assumed ‘{}’ in there) I realized that that last example wouldn't exactly work for you, because the 2nd and 3rd arguments are one string. So I added the clarification, and (yet another!) parallel, to demonstrate how you'd run your Python simulator.

$ parallel parallel --link --arg-sep ,,, -I [] echo {1} [] ,,, {0..3} ,,, {100..450..50} ::: {10..20..10} | parallel -C' ' echo foo {1} bar {2} blat {3}
foo 10 bar 0 blat 100
foo 10 bar 1 blat 150
foo 10 bar 2 blat 200
foo 10 bar 3 blat 250
foo 10 bar 1 blat 350
foo 10 bar 0 blat 300
foo 10 bar 2 blat 400
foo 10 bar 3 blat 450
foo 20 bar 0 blat 100
foo 20 bar 1 blat 150
foo 20 bar 2 blat 200
foo 20 bar 3 blat 250
foo 20 bar 0 blat 300
foo 20 bar 1 blat 350
foo 20 bar 2 blat 400
foo 20 bar 3 blat 450

For any enumerated list of values

$ parallel parallel --link --arg-sep ,,, -I [] echo {1} [] ,,, {0..3} ,,, v0.0 v0.1 v0.2 v0.3 v1.0 v1.1 v1.2 v1.3 ::: {10..20..10} | parallel -C' ' echo power {1} seed {2} num {3}
power 20 seed 0 num v0.0
power 20 seed 1 num v0.1
power 20 seed 2 num v0.2
power 20 seed 3 num v0.3
power 20 seed 0 num v1.0
power 20 seed 1 num v1.1
power 20 seed 2 num v1.2
power 20 seed 3 num v1.3
power 10 seed 0 num v0.0
power 10 seed 1 num v0.1
power 10 seed 2 num v0.2
power 10 seed 3 num v0.3
power 10 seed 0 num v1.0
power 10 seed 1 num v1.1
power 10 seed 2 num v1.2
power 10 seed 3 num v1.3

This is getting to be a very long answer. I think maybe you want something more like this, where 1 through 12 (number of powers times number of seeds) are the unique values for each combination of power and seed, and could be an enumerated list of values rather than {1..12}? Note I'm linking power and seed rather than num and seed.

$ parallel --link echo {1} {2} ::: "$(parallel echo {1} {2} ::: {10..30..10} ::: {0..3})" ::: {1..12} | parallel -C' ' echo run-sim --power {1} --seed {2} --num {3}
run-sim --power 10 --seed 0 --num 1
run-sim --power 10 --seed 1 --num 2
run-sim --power 10 --seed 2 --num 3
run-sim --power 10 --seed 3 --num 4
run-sim --power 20 --seed 0 --num 5
run-sim --power 20 --seed 1 --num 6
run-sim --power 20 --seed 2 --num 7
run-sim --power 20 --seed 3 --num 8
run-sim --power 30 --seed 0 --num 9
run-sim --power 30 --seed 1 --num 10
run-sim --power 30 --seed 2 --num 11
run-sim --power 30 --seed 3 --num 12
4
  • I was expecting a solution using multiple parallel would come, and that is completely fine. However, there is a small mistake here, that the num values are not always in multiples in 50. I was being quite lazy and ended up using example values in an easy to guess pattern
    – MaJoR21
    Apr 9, 2020 at 21:07
  • Think of it like this way, for each power value, there is a unique array of num values where the index of the array corresponds to the seed value. Say seed == 0 then for power = 10, the num value can be a number say x. For power = 20 (seed == 0 only) the num value would be y
    – MaJoR21
    Apr 9, 2020 at 21:09
  • I added another example with enumerated values. Does that accomplish what you want?
    – Larry
    Apr 10, 2020 at 18:30
  • Yes, that was exactly what I wanted. I realized that I could flatten the 2D array I was talking about earlier and use it as you have done for num ({1..12}). Thanks a lot for your answer. It sure is very helpful
    – MaJoR21
    Apr 11, 2020 at 4:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .