255 lines
6.9 KiB
Python
255 lines
6.9 KiB
Python
"""RESS.py
|
|
|
|
Random Equal Stimulus Sets
|
|
|
|
Originally coded in Object Pascal for Delphi by Wesley R. Elsberry
|
|
around 1999.
|
|
|
|
Translation to Python 3 by ChatGPT (GPT-4) 2023-06-01.
|
|
|
|
Random Equal Stimulus Sets are sequences of numbers indicating one of
|
|
a set of stimuli to be presented to a subject in a cognitive or
|
|
psychophysics task. The basic rules for generating these sequences is
|
|
derived from Gellermann 1925(?), but modified to permit the
|
|
specification of more than two stimuli in the set. The restriction on
|
|
a maximum of three sequential presentations of the same stimulus is
|
|
retained.
|
|
|
|
Issues:
|
|
The 'next_yield' method does not work.
|
|
Using 'next' for a sequence longer than the defined length of
|
|
sequence can cause there to be sequences that violate Gellermann's
|
|
assumptions, as the sequences composed together are not tested
|
|
across the joins.
|
|
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
import traceback
|
|
|
|
import random
|
|
|
|
MAXRESS = 120 # Arbitrary maximum
|
|
|
|
class RESS:
|
|
"""
|
|
RESS class represents the equivalent of the Pascal unit 'ress' in Python.
|
|
|
|
Random Equal Stimulus Sets are sequences of numbers indicating one of
|
|
a set of stimuli to be presented to a subject in a cognitive or
|
|
psychophysics task. The basic rules for generating these sequences is
|
|
derived from Gellermann 1925(?), but modified to permit the
|
|
specification of more than two stimuli in the set. The restriction on
|
|
a maximum of three sequential presentations of the same stimulus is
|
|
retained.
|
|
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.classes = None
|
|
self.thelength = None
|
|
self.series = [0] * MAXRESS
|
|
self.lastseries = [0] * MAXRESS
|
|
self.cnt = None
|
|
self.seriesstr = ""
|
|
self.current = None
|
|
self.dummy = None
|
|
self.hist = [0] * 61
|
|
|
|
def init(self):
|
|
"""
|
|
Initializes the variables in TRESS.
|
|
"""
|
|
self.classes = 1
|
|
self.thelength = 0
|
|
self.series = [0] * MAXRESS
|
|
self.lastseries = [0] * MAXRESS
|
|
self.hist = [0] * 61
|
|
self.cnt = 0
|
|
self.seriesstr = ""
|
|
self.dummy = 0
|
|
|
|
def makestring(self):
|
|
"""
|
|
Creates a string representation of the series.
|
|
Returns:
|
|
The string representation of the series.
|
|
"""
|
|
tstr = ""
|
|
for val in self.series[1:self.thelength + 1]:
|
|
tstr += str(val)
|
|
self.seriesstr = tstr
|
|
return tstr
|
|
|
|
def generate(self, len, nclass):
|
|
"""
|
|
Generates a candidate series.
|
|
Args:
|
|
len: The length of the series.
|
|
nclass: The number of classes.
|
|
"""
|
|
self.cnt = 0
|
|
self.classes = nclass
|
|
|
|
# Constraint: sequence length less than maximum
|
|
if MAXRESS >= len:
|
|
self.thelength = len
|
|
else:
|
|
self.thelength = MAXRESS
|
|
|
|
# Constraint: Multiple of number of classes
|
|
if self.thelength % self.classes != 0:
|
|
self.thelength -= self.thelength % self.classes
|
|
|
|
for i in range(self.classes):
|
|
self.hist[i] = self.thelength // self.classes
|
|
|
|
self.series[0] = random.randint(0, self.classes - 1)
|
|
self.hist[self.series[0]] -= 1
|
|
|
|
run = 1
|
|
for i in range(1, self.thelength):
|
|
ctr = 0
|
|
while True:
|
|
ctr += 1
|
|
jj = random.randint(0, self.classes - 1)
|
|
if self.hist[jj] > 0:
|
|
shortrun = (self.series[i - 1] == jj and run < 3) or (self.series[i - 1] != jj)
|
|
break
|
|
if ctr > 100:
|
|
break
|
|
if self.series[i - 1] == jj:
|
|
run += 1
|
|
else:
|
|
run = 1
|
|
self.hist[jj] -= 1
|
|
self.series[i] = jj
|
|
|
|
def test(self):
|
|
"""
|
|
Tests candidates for criteria.
|
|
Returns:
|
|
True if the series is valid, False otherwise.
|
|
"""
|
|
ok = True
|
|
hist = [0] * 61
|
|
|
|
for val in self.series[:self.thelength]:
|
|
hist[val] += 1
|
|
|
|
for i in range(self.classes - 1):
|
|
if hist[i] != hist[i + 1]:
|
|
ok = False
|
|
|
|
if ok:
|
|
run = 1
|
|
for i in range(1, self.thelength):
|
|
if self.series[i - 1] == self.series[i]:
|
|
run += 1
|
|
if run > 3:
|
|
ok = False
|
|
else:
|
|
run = 1
|
|
|
|
return ok
|
|
|
|
def newress(self, nlen=24, nclass=2):
|
|
"""
|
|
Finds and saves a valid series using generate and test.
|
|
Args:
|
|
nlen: The length of the series.
|
|
nclass: The number of classes.
|
|
"""
|
|
print('nlen', nlen, 'nclass', nclass)
|
|
try:
|
|
|
|
random.seed()
|
|
|
|
self.lastseries = self.series
|
|
|
|
while True:
|
|
self.generate(nlen, nclass)
|
|
# print("gen", self.makestring())
|
|
if self.test():
|
|
break
|
|
return self.makestring()
|
|
except:
|
|
estr = f"Error: {traceback.format_exc()}"
|
|
print(estr)
|
|
return ''
|
|
|
|
def next(self):
|
|
"""
|
|
Returns the next value within a series.
|
|
Returns:
|
|
The next value in the series.
|
|
"""
|
|
if self.cnt >= self.thelength:
|
|
self.newress(self.thelength, self.classes)
|
|
|
|
self.cnt += 1
|
|
self.current = self.series[self.cnt]
|
|
return self.series[self.cnt]
|
|
|
|
def next_yield(self):
|
|
"""
|
|
Yields the next value within a series.
|
|
"""
|
|
print('start', self.series, self.cnt, self.series[self.cnt])
|
|
|
|
while True:
|
|
if self.cnt >= self.thelength:
|
|
print("calling newress")
|
|
self.newress(self.thelength, self.classes)
|
|
self.cnt = 0
|
|
|
|
print(self.cnt)
|
|
print(self.series, self.cnt, self.series[self.cnt])
|
|
self.current = self.series[self.cnt]
|
|
yield str(self.current)
|
|
self.cnt += 1
|
|
|
|
# Exercise the TRESS code
|
|
|
|
from random import seed
|
|
|
|
def main():
|
|
# Set the seed for random number generation
|
|
seed()
|
|
|
|
# Create an instance of the TRESS class
|
|
ress1 = RESS()
|
|
|
|
# Initialize the TRESS object
|
|
ress1.init()
|
|
|
|
# Generate and print a valid series
|
|
ress1.newress(24, 3)
|
|
series = ress1.makestring()
|
|
print("Generated Series:", series)
|
|
|
|
ress1.newress(24, 3)
|
|
series = ress1.makestring()
|
|
print("Generated Series:", series)
|
|
|
|
ress1.newress(24, 3)
|
|
series = ress1.makestring()
|
|
print("Generated Series:", series)
|
|
|
|
ress1.newress(24, 3)
|
|
series = ress1.makestring()
|
|
print("Generated Series:", series)
|
|
|
|
ress1.newress(24, 3)
|
|
series = ress1.makestring()
|
|
print("Generated Series:", series)
|
|
|
|
# Generate and print the next value in the series
|
|
for ii in range(26):
|
|
next_val = ress1.next()
|
|
print(ii, "Next Value:", str(next_val))
|
|
|
|
if __name__ == "__main__":
|
|
main()
|