Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

seed ops #176

Merged
merged 13 commits into from Mar 27, 2019
Merged

seed ops #176

merged 13 commits into from Mar 27, 2019

Conversation

@alpha-cactus
Copy link
Contributor

@alpha-cactus alpha-cactus commented Jan 13, 2019

What does this PR do?

changes the random number generator for all ops using stdlib rand() to use libavr32 rand_state_t. add SEED op to set seed for all ops using rand. add separate ops to set seed for any individual op.

list of all added ops including aliases:
SEED
RAND.SEED / RAND.SD / R.SD
TOSS.SEED / TOSS.SD
PROB.SEED / PROB.SD
DRUNK.SEED / DRUNK.SD
P.SEED / P.SD

the algorithm used by rand_state_t comes from here(page 10):
numerical recipes

Provide links to any related discussion on lines.

How should this be manually tested?

I've been running these ops through multiple scripts the past few weeks. also wrote a simple teletype script to test that the seed setting functionality works. confirmed the stream of number generated is the same when setting the same seed.

only issue I found in testing is that the LSB oscillates between 0 and 1. I had to update the TOSS op to test bit 4 because of this. note this does cause something like the following to oscillate between the min/max number:
RRAND 8 9
will flip between returning 8 and 9. I understand this may be a serious concern. if so I can work on finding a better pnrg algorithm that can be seeded.

Any background context you want to provide?

added new file seed.c to house seed ops. added macro MAKE_SEED_OP for creating seed ops in 1 line. the macro functions similar to the MAKE_SIMPLE_VARIABLE_OP.

because all the seed ops are using the same function aliases are also made using the MAKE_SEED_OP macro.

used a union for housing the random_state_t structures for each op. this allows them to be accessed individually by nam, or iterated over for initialization and the SEED op.

If the related Github issues aren't referenced in your commits, please link to them here.

I have,

  • updated CHANGELOG.md
  • updated the documentation
  • run make format on each commit
@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Jan 14, 2019

also wanted to note this PR relies on this change made to libavr 32:
libavr32

I noticed that the teletype libavr32 still links to the version before this change. not exactly sure how recursive repositories work.

"DRUNK.SD" => { MATCH_OP(E_OP_SYM_DRUNK_SD); };
"P.SEED" => { MATCH_OP(E_OP_P_SEED); };
"P.SD" => { MATCH_OP(E_OP_SYM_P_SD); };

This comment has been minimized.

@scanner-darkly

scanner-darkly Jan 15, 2019
Member

indentation seems off?

This comment has been minimized.

@alpha-cactus

alpha-cactus Jan 15, 2019
Author Contributor

strange. I ran git-clang-format. will investigate when I get home later.

This comment has been minimized.

@alpha-cactus

alpha-cactus Jan 16, 2019
Author Contributor

ya looks entirely fine on my end(running vim).

screen shot 2019-01-15 at 7 11 22 pm

This comment has been minimized.

@scanner-darkly

scanner-darkly Jan 16, 2019
Member

it's likely due to tabs being used which i think clang-format is supposed to convert (teletype uses 4 spaces convention)

This comment has been minimized.

@scanner-darkly

scanner-darkly Jan 16, 2019
Member

this is what i'm seeing on github:

image

This comment has been minimized.

@alpha-cactus

alpha-cactus Jan 16, 2019
Author Contributor

oh okay I think I'm using 2 spaces because that's norns convention.

const tele_op_t op_DRUNK_SEED = MAKE_SEED_OP(DRUNK.SEED, rand_states.s.drunk);
const tele_op_t op_SYM_DRUNK_SD = MAKE_SEED_OP(DRUNK.SD, rand_states.s.drunk);
const tele_op_t op_P_SEED = MAKE_SEED_OP(P.SEED, rand_states.s.pattern);
const tele_op_t op_SYM_P_SD = MAKE_SEED_OP(P.SD, rand_states.s.pattern);

This comment has been minimized.

@scanner-darkly

scanner-darkly Jan 15, 2019
Member

indentation seems off?

This comment has been minimized.

@alpha-cactus

alpha-cactus Jan 15, 2019
Author Contributor

yup probably some sort of tab issue. will fix.

exec_state_t *NOTUSED(es), command_state_t *cs) {
char *base = (char *)ss;
size_t offset = (size_t)data;
rand_set_t *ptr = (rand_set_t *)(base + offset);

This comment has been minimized.

@scanner-darkly

scanner-darkly Jan 15, 2019
Member

does this depend on specific order of members in scene_state_t?

This comment has been minimized.

@alpha-cactus

alpha-cactus Jan 15, 2019
Author Contributor

no I don't think order matters. I'm using the same technique as the MAKE_SIMPLE_VARIABLE_OP macro, but the warning comment for variable order is related to something else.

https://llllllll.co/t/teletype-firmware-discussion/13961/38?u=alphacactus

src/state.h Outdated Show resolved Hide resolved
@scanner-darkly
Copy link
Member

@scanner-darkly scanner-darkly commented Jan 15, 2019

great, added some comments!

re: libavr32 - i assume you want to update it to monome/libavr32@377bc18? to do so go to libavr32 folder and do a git fetch and check out the commit you need. once you do this it will show up as a change in your teletype repo, and you can commit it as a regular commit (what it will do is basically update the commit the teletype repo is referencing).

for more info on working with submodule check out the guide @samdoshi put together: https://samdoshi.com/post/2016/03/git-submodules/

@@ -240,6 +247,26 @@ void op_poke_i16(const void *data, scene_state_t *ss, exec_state_t *NOTUSED(es),
tele_vars_updated();
}

void op_peek_seed_i16(const void *data, scene_state_t *ss,

This comment has been minimized.

@alpha-cactus

alpha-cactus Jan 15, 2019
Author Contributor

actually can probably move these and the macros into seed.c

@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Jan 16, 2019

only issue I found in testing is that the LSB oscillates between 0 and 1. I had to update the TOSS op to test bit 4 because of this. note this does cause something like the following to oscillate between the min/max number:
RRAND 8 9
will flip between returning 8 and 9 at random. I understand this may be a serious concern. if so I can work on finding a better pnrg algorithm that can be seeded.

still concerned about this.

@scanner-darkly
Copy link
Member

@scanner-darkly scanner-darkly commented Jan 16, 2019

not sure i fully understand - are you saying it doesn't respect the seed?

@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Jan 16, 2019

only issue I found in testing is that the LSB oscillates between 0 and 1. I had to update the TOSS op to test bit 4 because of this. note this does cause something like the following to oscillate between the min/max number:
RRAND 8 9
will flip between returning 8 and 9. I understand this may be a serious concern. if so I can work on finding a better pnrg algorithm that can be seeded.

actually don't think I was clear in my original post. output would look like this.

8
9
8
9
8
9

which probably isn't good. if we can eliminate the LSB from the equation it should be fine. maybe a bit SHIFT and OR? although at that point is it worth the trouble.

@alpha-cactus alpha-cactus reopened this Jan 16, 2019
@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Jan 16, 2019

ya sorry post got cut off early.

@scanner-darkly
Copy link
Member

@scanner-darkly scanner-darkly commented Jan 16, 2019

ah i see, so the least significant bit is not random. does this apply to bigger ranges too? say if i did RRND 0 9 would it return odd/even/odd/even/odd etc?

@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Jan 16, 2019

yes that's why I had to change TOSS to test a different bit. probably wouldn't be hard to find a better tiny prng algorithm with some research as long as the rest of the code holds up to review.

@scanner-darkly
Copy link
Member

@scanner-darkly scanner-darkly commented Jan 16, 2019

yeah we could try right shifting it by 1 or XORing 2 numbers but that might also affect the quality of the algorithm..

@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Jan 16, 2019

ya looking at it now probably premature for this PR given the issue. I guess I wanted to see what others had to say on it. can probably close this for now and just have a single SEED op using stdlib srand() until I come up with something better.

@scanner-darkly
Copy link
Member

@scanner-darkly scanner-darkly commented Jan 16, 2019

let's leave it open - these are fantastic ops and very useful, we just need to find a better algo!
so if i understand correctly the issue with using srand is that you only get one seed?

@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Jan 16, 2019

let's leave it open - these are fantastic ops and very useful, we just need to find a better algo!

ok cool I had already been looking at a few should be able to come up with something before the weekend.

so if i understand correctly the issue with using srand is that you only get one seed?

yes that's my understanding of it.

@scanner-darkly
Copy link
Member

@scanner-darkly scanner-darkly commented Jan 16, 2019

in this case one way would be to reproduce the implementation but with the ability to maintain several states/seeds, and add an index parameter to rand/srand

@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Jan 16, 2019

oh ya hadn't really considered that! I'll look into it if not just to see how it works.

@scanner-darkly
Copy link
Member

@scanner-darkly scanner-darkly commented Jan 16, 2019

doing a quick search, this: http://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/random_r.c;hb=glibc-2.15#l361 has the ability to switch states. also this: http://www.cse.yorku.ca/~oz/marsaglia-rng.html has several algos that might work?

@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Jan 16, 2019

yes! didn't have much time last night, but I literally book marked those exact two pages for reading today.

@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Jan 17, 2019

code is a little dirty, but I think the Marsaglia KISS algorithm seems sufficient. still uses a Linear Congruential Generator like libavr32 so it can be seeded. however the MWC and SHR3 algorithms improve the randomness of the lower bits. I'll work it in to the teletype firmware if it looks good to you.

initial testing looks good. from what I can tell.
https://github.com/alpha-cactus/prng/blob/master/tele_rand.c

@scanner-darkly
Copy link
Member

@scanner-darkly scanner-darkly commented Jan 17, 2019

i only have basic knowledge about random generators, so i'm good with whatever you think will work best!

@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Jan 19, 2019

alright this should be the final revision. tele_rand_t uses the MWC(multiply with carry) prng. it returns a 32bit signed integer between 0x0 and 0x7FFFFFFF which is the same range as what the stdlib rand() call returns.

I could move tele_rand.c/h stuff to the seed.c/h files if you want.

give me a few days to run this through some tests before merging.

command_state_t *cs);

// clang-format off
const tele_op_t op_SEED = MAKE_GET_SET_OP(SEED, op_SEED_get, op_SEED_set, 0, true);

This comment has been minimized.

@scanner-darkly

scanner-darkly Jan 20, 2019
Member

indentation off

@@ -96,7 +98,8 @@ typedef struct {
uint8_t time_act;
int16_t tr[TR_COUNT];
int16_t tr_pol[TR_COUNT];
int16_t tr_time[TR_COUNT];
int16_t tr_time[TR_COUNT];

This comment has been minimized.

@scanner-darkly

scanner-darkly Jan 20, 2019
Member

indentation off?

This comment has been minimized.

@alpha-cactus

alpha-cactus Jan 20, 2019
Author Contributor

ugh. fixed what was causing this, so it shouldn't be an issue going forward. I went through my previous commits & pull requests and have hopefully fixed everything now. must've missed this one.

This comment has been minimized.

@scanner-darkly

scanner-darkly Mar 27, 2019
Member

looks like indentation is still off here and on the next line

exec_state_t *NOTUSED(es), command_state_t *cs) {
cs_push(cs, rand() & 1);
tele_rand_t *r = &ss->rand_states.s.toss;
cs_push(cs, (tele_rand(r) >> 3) & 1);

This comment has been minimized.

@scanner-darkly

scanner-darkly Jan 20, 2019
Member

do we still need to use a different bit with the latest algo?

This comment has been minimized.

@alpha-cactus

alpha-cactus Jan 20, 2019
Author Contributor

probably not. I think the issue with less random lower bits is only tied to the linear congruential generator algorithm. I'll run some tests using the new algorithm with the lsb to verify.

@scanner-darkly
Copy link
Member

@scanner-darkly scanner-darkly commented Jan 20, 2019

added some minor comments, other than that looks good!
re: tele_rand.c/h - i think libavr32 might be a better place for it, since then other firmwares can take advantage of it too.

@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Jan 20, 2019

added some minor comments, other than that looks good!
re: tele_rand.c/h - i think libavr32 might be a better place for it, since then other firmwares can take advantage of it too.

ya I'm curious how many firmwares(if any) are using the current libavr32 random. I'd prefer updating it rather than adding a second random function to the library. should be simple enough to just replace the algorithm without changing any of the other functionality.

@scanner-darkly
Copy link
Member

@scanner-darkly scanner-darkly commented Jan 21, 2019

i did a search in trilogy/ansible/aleph repos and looks like the only thing that uses random_next is arp_seq_build_random which is used by arp_seq_build (both are in libavr32/src/arp.c) which is only used by ansible. so yeah i think replacing it with the new implementation should be fine.

@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Jan 24, 2019

@scanner-darkly
algorithm tests both manual/automated seem fine so far. wanted to get your opinion on the functionality of random_state_t. currently it can be instantiated with a min/max range, but the teletype firmware already handles that. so I'm debating whether to remove it as part of the libavr32 change.

link to my current version:
https://github.com/alpha-cactus/libavr32/blob/master/src/random.c
previous version:
https://github.com/monome/libavr32/blob/master/src/random.c

new version would leave range limiting responsibility to the caller. I could keep the range functionality, but would want to then probably remove it from the teletype firmware where possible since modulus operations are kind of expensive if I recall.

@scanner-darkly
Copy link
Member

@scanner-darkly scanner-darkly commented Jan 27, 2019

i would leave it in, unless that involves more changes, otherwise we'll have to check if anything uses it (and i don't think modulus would be a huge concern performance wise in the big scheme of things). why would you need to remove it from teletype though? so it doesn't do it twice?

@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Jan 27, 2019

well the libavr32 algorithm generates a number in the range 0x0-0x7FFFFFF. then that number is cut down to the specified min/max range and returned to teletype. in the case of an op like RRAND the returned value would then again be cut down to the range specified by the parameters the user puts in.

ya I was thinking maybe we could have libavr32 just perform all the range limiting. however, that might just reduce code readability/maintainability for what ends up being a really small optimization.

@scanner-darkly
Copy link
Member

@scanner-darkly scanner-darkly commented Jan 27, 2019

wouldn't libavr32 range limiting apply to all generated numbers? so you'd still need to apply it for RRND. so for teletype it doesn't make sense to use it, but i'm not sure if any other firmware that uses libavr32 does or not.

@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Jan 27, 2019

right it's currently built into the random_next function so there's no option to not specify a min/max range. I agree it doesn't make sense to perform a min/max range mod twice. currently as far as I can tell the only other firmware that uses random_next is the libavr32 arp.c. it would be relatively simple to modify arp.c if I were to get rid of the range mod from random_next.

https://github.com/monome/libavr32/blob/377bc181a81ed616647f2cb644f662711238e579/src/random.c#L26

@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Feb 7, 2019

ran some quick tests to confirm the libavr32 integration is good. bit of a wild PR thanks for sticking with it.

@scanner-darkly
Copy link
Member

@scanner-darkly scanner-darkly commented Feb 7, 2019

thanks for doing it!
@tehn - did you want to take a look?

@alpha-cactus
Copy link
Contributor Author

@alpha-cactus alpha-cactus commented Feb 8, 2019

I think the indentation is actually still off...

let me fix that first.

edit:
actually maybe it's fine? looked off on my phone.

@scanner-darkly
Copy link
Member

@scanner-darkly scanner-darkly commented Mar 27, 2019

ah sorry, didn't see your edit of the previous message - github doesn't send notification for edits. yeah indentation looks fine to me except a couple of lines, i left a comment there.

@tehn
tehn approved these changes Mar 27, 2019
Copy link
Member

@tehn tehn left a comment

fantastic!

@tehn tehn merged commit 75eda8b into monome:master Mar 27, 2019
1 check passed
1 check passed
continuous-integration/travis-ci/pr The Travis CI build passed
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

3 participants