Example #1
0
    def test_constraintMinDist(self):

        self.setUp()
        ef = Enforce(self.dm)
        ef.add_constraint(MinDist, cols=[self.dm.word], mindist=3)
        dm = ef.enforce()
        for row in range(3, len(dm)):
            s = dm.word[row - 3:row].unique
            self.assertTrue(len(s) == 3)
Example #2
0
    def test_constraintMaxRep(self):

        self.setUp()
        ef = Enforce(self.dm)
        ef.add_constraint(MaxRep, maxrep=1)
        dm = ef.enforce()
        for row in range(len(dm)):
            for cell1, cell2 in zip(dm[row], dm[row - 1]):
                self.assertTrue(cell1 != cell2)
Example #3
0
def applyConstraints(dm_exp, dm_fillers):
    

    """
    Arguments:
    dm_exp        --- dm containing exp scene-object combinations
    dm_fillers    --- dm containing filler scene-object combinations
    
    Returns:
    final_dm_test_feedback        --- final datamatrix for the test feedback blockloop
    final_dm_criterion_test        --- final datamatrix for the test feedback blockloop
    """
    
    
    # Add distractors:
    dm_exp = addDistractors.addDistractors(dm_exp)
    dm_fillers = addDistractors.addDistractors(dm_fillers)
    
    # Make sure we will collect two dms
    list_dms = []
    
    for block in ["test_feedback", "criterion_test"]:
    
        # Shuffle the fillers:
        dm_fillers = ops.shuffle(dm_fillers)
        
        # Merge first 8 fillers to the main dm
        # NOTE: we leave out four fillers because we will use them for the
        # very first and the very last trials
        dm_fillers_slice = dm_fillers[0:8]
        main_dm = dm_exp << dm_fillers_slice
        
        # Shuffle the main dm:
        main_dm = ops.shuffle(main_dm)
        
        # Create an Enforce object, and add two constraints
        ef = Enforce(main_dm)
        ef.add_constraint(MaxRep, cols=[main_dm.Emotion], maxrep=3)
        ef.add_constraint(MinDist, cols=[main_dm.Trial_ID], mindist=2)
        
        # Enforce the constraints
        main_dm = ef.enforce()
        # See the resulting DataFrame and a report of how long the enforcement took.
        
        
        # Add two fillers to the beginning of the final dm:
        dm_fillers_slice_first = dm_fillers[8:10]
        final_dm = dm_fillers_slice_first << main_dm
        
        # And to the end fo the final dm:
        dm_fillers_slice_last = dm_fillers[10:]
        final_dm = final_dm << dm_fillers_slice_last
        
        # Append to the dm list:
        list_dms.append(final_dm)

    # Unpack the list so we can return two separate dms
    dm1, dm2 = list_dms

    return dm1, dm2
def applyConstraints(dm_exp, dm_fillers):
    """
    Arguments:
    dm_exp        --- dm containing exp scene-object combinations
    dm_fillers    --- dm containing filler scene-object combinations
    
    Returns:
    final_dm        --- final datamatrix for the IMDF MC memory task
    """

    # Add distractors:
    dm_exp = addDistractorsImdf.addDistractors(dm_exp)
    dm_fillers = addDistractorsImdf.addDistractors(dm_fillers)

    # Shuffle the fillers:
    dm_fillers = ops.shuffle(dm_fillers)

    # Merge first 4 fillers to the main dm
    # NOTE: we leave out four fillers because we will use them for the
    # very first and the very last trials

    # TODO: select 2 neg en 2 neutral
    dm_fillers_slice = dm_fillers[0:4]
    main_dm = dm_exp << dm_fillers_slice

    # Shuffle the main dm:
    main_dm = ops.shuffle(main_dm)

    # Create an Enforce object, and add two constraints
    ef = Enforce(main_dm)
    ef.add_constraint(MaxRep, cols=[main_dm.Emotion], maxrep=3)
    ef.add_constraint(MinDist, cols=[main_dm.Trial_ID], mindist=2)

    # Enforce the constraints
    main_dm = ef.enforce()
    # See the resulting DataFrame and a report of how long the enforcement took.

    # Add two fillers to the beginning of the final dm:
    dm_fillers_slice_first = dm_fillers[4:6]
    final_dm = dm_fillers_slice_first << main_dm

    # And to the end fo the final dm:
    dm_fillers_slice_last = dm_fillers[6:]
    final_dm = final_dm << dm_fillers_slice_last

    return final_dm
Example #5
0
    for pp_ID in range(1, 71):

        main_dm = io.readtxt(src_exp)

        # Read in fillers:
        dm = io.readtxt(src_fillers)
        dm_first, dm_last, dm_remaining = helpers.splitFillers(dm)

        # Merge the remaining fillers with the main dm:
        merged_dm = main_dm << dm_remaining

        # Shuffle the merged dm:
        merged_dm = ops.shuffle(merged_dm)

        # Create an Enforce object, and add constraint
        ef = Enforce(merged_dm)
        ef.add_constraint(MaxRep, cols=[merged_dm.Emotion], maxrep=3)
        ef.add_constraint(MinDist, cols=[merged_dm.Trial_ID], mindist=2)

        # Enforce the constraints
        merged_dm = ef.enforce()

        # Add the first fillers:
        merged_dm = dm_first << merged_dm
        # Add the last fillers:
        merged_dm = merged_dm << dm_last

        # Add exp ID to the dm:
        merged_dm["Exp_ID"] = block
        io.writetxt(merged_dm, os.path.join(dst, "blockloop_%s_PP_%s.csv" \
            % (block, pp_ID)))
Example #6
0
# Read a datafile with Pandas and convert it to a pseudorandom DataFrame.
TNTpseudM = io.readtxt('/Users/claudius/PycharmProjects2/EXPdesignOP/filepool/TNTrun2csv.csv')

# optimisation loop

Fe_threshold = 13#14.3 #(15)
Fd_threshold = 6#6.3 #(6.5)
Ff_threshold = 0.9 #(1)
Fc_threshold = 0.8 #(0.8)

timeout = time.time() + 60*5 # 5 minutes from now

while True:

    # Create an Enforce object and add constraints
    ef = Enforce(TNTpseudM)
    ef.add_constraint(MaxRep,cols=[TNTpseudM.T_ID],maxrep=3)
    ef.add_constraint(MaxRep,cols=[TNTpseudM.NT_ID],maxrep=3)
    ef.add_constraint(MaxRep,cols=[TNTpseudM.neu_ID],maxrep=3)
    ef.add_constraint(MaxRep,cols=[TNTpseudM.neg_ID],maxrep=3)
    ef.add_constraint(MinDist,cols=[TNTpseudM.null_ID],mindist=2)

    # Enforce the constraints
    TNTpseudM = ef.enforce()

    # convert new datamatrix to a list as input for DES1
    TNTorder = list(TNTpseudM.order)

    TNTdES = neurodesign.classes.design(
        order = TNTorder,
        ITI = generate.iti(ntrials=124, model="exponential", min=1.4, mean=2, max=2.6,
Example #7
0
	def from_string(self, string):

		"""See item."""

		self.var.clear()
		self.comments = []
		self.reset()
		if string is None:
			return
		for i in string.split(u'\n'):
			self.parse_variable(i)
			cmd, arglist, kwdict = self.syntax.parse_cmd(i)
			if cmd == u'run':
				if len(arglist) != 1 or kwdict:
					raise osexception(u'Invalid run command: %s' % i)
				self._item = arglist[0]
				continue
			if cmd == u'setcycle':
				if self.ef is not None or self.operations:
					raise osexception(
						u'setcycle must come before constraints and operations')
				if len(arglist) != 3 or kwdict:
					raise osexception(u'Invalid setcycle command: %s' % i)
				row, var, val = tuple(arglist)
				if row >= len(self.dm):
					self.dm.length = row+1
				if var not in self.dm:
					self.dm[var] = u''
				self.dm[row][var] = val
				continue
			if cmd == u'constrain':
				if self.operations:
					raise osexception(
						u'constraints must come before operations')
				if self.ef is None:
					self.ef = Enforce(self.dm)
				if len(arglist) != 1:
					raise osexception(u'Invalid constrain command: %s' % i)
				colname = arglist[0]
				try:
					col = self.dm[colname]
				except:
					raise osexception(u'Invalid column name: %s' % colname)
				for constraint, value in kwdict.items():
					try:
						if constraint == u'maxrep':
							self.ef.add_constraint(MaxRep, cols=[col],
								maxrep=value)
							continue
						if constraint == u'mindist':
							self.ef.add_constraint(MinDist, cols=[col],
								mindist=value)
							continue
					except InvalidConstraint as e:
						raise osexception(e)
					raise osexception(u'Invalid constrain command: %s' % i)
				continue
			if cmd in self.commands:
				self.operations.append( (cmd, arglist) )
		if len(self.dm) == 0:
			self.dm.length = 1
		if len(self.dm.columns) == 0:
			self.dm.empty_column = u''
		# Backwards compatibility: Older version of OpenSesame can specify the
		# number of cycles through the cycles variable. If the specified number
		# of cycles doesn't match the length of the datamatrix, we change the
		# length of the datamatrix.
		if u'cycles' in self.var and isinstance(self.var.cycles, int) \
				and self.var.cycles != len(self.dm):
			self.dm.length = self.var.cycles
Example #8
0
class loop(item.item):

	"""A loop item runs a single other item multiple times"""

	description = u'Repeatedly runs another item'
	valid_orders = u'sequential', u'random'
	commands = [
		u'fullfactorial',
		u'shuffle',
		u'shuffle_horiz',
		u'slice',
		u'sort',
		u'sortby',
		u'reverse',
		u'roll',
		u'weight',
		]

	def reset(self):

		"""See item."""

		self.ef = None
		self.dm = DataMatrix(length=0)
		self.live_dm = None
		self.live_row = None
		self.operations = []
		self._item = u''

		self.var.repeat = 1
		self.var.continuous = u'no'
		self.var.order = u'random'
		self.var.break_if = u'never'
		self.var.break_if_on_first = u'yes'
		self.var.source = u'table' # file or table
		self.var.source_file = u''

	def from_string(self, string):

		"""See item."""

		self.var.clear()
		self.comments = []
		self.reset()
		if string is None:
			return
		for i in string.split(u'\n'):
			self.parse_variable(i)
			cmd, arglist, kwdict = self.syntax.parse_cmd(i)
			if cmd == u'run':
				if len(arglist) != 1 or kwdict:
					raise osexception(u'Invalid run command: %s' % i)
				self._item = arglist[0]
				continue
			if cmd == u'setcycle':
				if self.ef is not None or self.operations:
					raise osexception(
						u'setcycle must come before constraints and operations')
				if len(arglist) != 3 or kwdict:
					raise osexception(u'Invalid setcycle command: %s' % i)
				row, var, val = tuple(arglist)
				if row >= len(self.dm):
					self.dm.length = row+1
				if var not in self.dm:
					self.dm[var] = u''
				self.dm[row][var] = val
				continue
			if cmd == u'constrain':
				if self.operations:
					raise osexception(
						u'constraints must come before operations')
				if self.ef is None:
					self.ef = Enforce(self.dm)
				if len(arglist) != 1:
					raise osexception(u'Invalid constrain command: %s' % i)
				colname = arglist[0]
				try:
					col = self.dm[colname]
				except:
					raise osexception(u'Invalid column name: %s' % colname)
				for constraint, value in kwdict.items():
					try:
						if constraint == u'maxrep':
							self.ef.add_constraint(MaxRep, cols=[col],
								maxrep=value)
							continue
						if constraint == u'mindist':
							self.ef.add_constraint(MinDist, cols=[col],
								mindist=value)
							continue
					except InvalidConstraint as e:
						raise osexception(e)
					raise osexception(u'Invalid constrain command: %s' % i)
				continue
			if cmd in self.commands:
				self.operations.append( (cmd, arglist) )
		if len(self.dm) == 0:
			self.dm.length = 1
		if len(self.dm.columns) == 0:
			self.dm.empty_column = u''
		# Backwards compatibility: Older version of OpenSesame can specify the
		# number of cycles through the cycles variable. If the specified number
		# of cycles doesn't match the length of the datamatrix, we change the
		# length of the datamatrix.
		if u'cycles' in self.var and isinstance(self.var.cycles, int) \
				and self.var.cycles != len(self.dm):
			self.dm.length = self.var.cycles

	def to_string(self):

		"""See item."""

		# Older versions of OpenSesame use an explicit cycles variable, so we
		# set this here for backwards compatibility.
		self.var.cycles = len(self.dm)
		s = item.item.to_string(self)
		for i, row in enumerate(self.dm):
			for name, val in row:
				s += u'\t%s\n' % \
					self.syntax.create_cmd(u'setcycle', [i, name, val])
		if self.ef is not None:
			d = {}
			for constraint in self.ef.constraints:
				col = constraint.cols[0]
				if col not in d:
					d[col] = {}
				if isinstance(constraint, MaxRep):
					d[col][u'maxrep'] = constraint.maxrep
				elif isinstance(constraint, MinDist):
					d[col][u'mindist'] = constraint.mindist
			for col, kwdict in d.items():
				s += u'\t%s\n' % self.syntax.create_cmd(u'constrain', [col],
					kwdict)
		for cmd, arglist in self.operations:
			s += u'\t%s\n' % self.syntax.create_cmd(cmd, arglist)
		s += u'\t%s\n' % self.syntax.create_cmd(u'run', [self._item])
		return s

	def _require_arglist(self, cmd, arglist, minlen=1):

		"""
		desc:
			Checks whether a non-empty argument list has been specified, and
			raises an Exception otherwise.

		arguments:
			cmd:
				desc:	The command to check for.
				type:	str
			arglist:
				desc:	The argument list to check.
				type:	list
		"""

		if len(arglist) < minlen:
			raise osexception(u'Invalid argument list for %s' % cmd)

	def _create_live_datamatrix(self):

		"""
		desc:
			Builds a live DataMatrix. That is, it takes the orignal DataMatrix
			and applies all the operations as specified.

		returns:
			desc:	A live DataMatrix.
			type:	DataMatrix
		"""

		if self.var.source == u'table':
			src_dm = self.dm
		else:
			from datamatrix import io
			src = self.experiment.pool[self.var.source_file]
			if src.endswith(u'.xlsx'):
				try:
					src_dm = io.readxlsx(src)
				except Exception as e:
					raise osexception(u'Failed to read .xlsx file: %s' % src,
						exception=e)
			else:
				try:
					src_dm = io.readtxt(src)
				except Exception as e:
					raise osexception(u'Failed to read text file (perhaps it has the wrong format or it is not utf-8 encoded): %s' % src,
						exception=e)
		for column_name in src_dm.column_names:
			if not self.syntax.valid_var_name(column_name):
				raise osexception(
					u'The loop table contains an invalid column name: 'u'\'%s\'' \
					% column_name)
		# The number of repeats should be numeric. If not, then give an error.
		# This can also occur when generating a preview of a loop table if
		# repeat is variable.
		if not isinstance(self.var.repeat, (int, float)):
			raise osexception(
				u'Don\'t know how to generate a DataMatrix for "%s" repeats' \
				% self.var.repeat)
		length = int(len(src_dm) * self.var.repeat)
		dm = DataMatrix(length=0)
		while len(dm) < length:
			i = min(length-len(dm), len(src_dm))
			if self.var.order == u'random':
				dm <<= operations.shuffle(src_dm)[:i]
			else:
				dm <<= src_dm[:i]
		if self.var.order == u'random':
			dm = operations.shuffle(dm)
		if self.ef is not None:
			self.ef.dm = dm
			dm = self.ef.enforce()
		for cmd, arglist in self.operations:
			# The column name is always specified last, or not at all
			if arglist:
				try:
					colname = arglist[-1]
					col = dm[colname]
				except:
					raise osexception(
						u'Column %s does not exist' % arglist[-1])
			if cmd == u'fullfactorial':
				dm = operations.fullfactorial(dm)
			elif cmd == u'shuffle':
				if not arglist:
					dm = operations.shuffle(dm)
				else:
					dm[colname] = operations.shuffle(col)
			elif cmd == u'shuffle_horiz':
				if not arglist:
					dm = operations.shuffle_horiz(dm)
				else:
					dm = operations.shuffle_horiz(
						*[dm[_colname] for _colname in arglist])
			elif cmd == u'slice':
				self._require_arglist(cmd, arglist, minlen=2)
				dm = dm[arglist[0]: arglist[1]]
			elif cmd == u'sort':
				self._require_arglist(cmd, arglist)
				dm[colname] = operations.sort(col)
			elif cmd == u'sortby':
				self._require_arglist(cmd, arglist)
				dm = operations.sort(dm, by=col)
			elif cmd == u'reverse':
				if not arglist:
					dm = dm[::-1]
				else:
					dm[colname] = col[::-1]
			elif cmd == u'roll':
				self._require_arglist(cmd, arglist)
				steps = arglist[0]
				if not isinstance(steps, int):
					raise osexception(u'roll steps should be numeric')
				if len(arglist) == 1:
					dm = dm[-steps:] << dm[:-steps]
				else:
					dm[colname] = list(col[-steps:]) + list(col[:-steps])
			elif cmd == u'weight':
				self._require_arglist(cmd, arglist)
				dm = operations.weight(col)
		return dm

	def prepare(self):

		"""See item."""

		item.item.prepare(self)
		# Deprecation errors. The direct reference to __vars__ prevents a lookup
		# in the parent var store.
		if (u'skip' in self.var.__vars__ and self.var.skip != 0) \
			or (u'offset' in self.var.__vars__ and self.var.offset != u'no'):
			raise osexception(
				u'The skip and offset options have been removed. Please refer to the documentation of the loop item for more information.')
		# Compile break-if statement
		break_if = self.var.get(u'break_if', _eval=False)
		if break_if not in (u'never', u''):
			self._break_if = self.syntax.compile_cond(break_if)
		else:
			self._break_if = None
		# Create a keyboard to flush responses between cycles
		self._keyboard = openexp.keyboard.keyboard(self.experiment)
		# Make sure the item to run exists
		if self._item not in self.experiment.items:
			raise osexception(
				u"Could not find item '%s', which is called by loop item '%s'" \
				% (self._item, self.name))

	def run(self):

		"""See item."""

		self.set_item_onset()
		if self.live_dm is None or self.var.continuous == u'no':
			self.live_dm = self._create_live_datamatrix()
			self.live_row = 0
		first = True
		while self.live_row < len(self.live_dm):
			self.experiment.var.repeat_cycle = 0
			self.experiment.var.live_row = self.live_row
			self.experiment.var.set('live_row_%s' % self.name, self.live_row)
			for name, val in self.live_dm[self.live_row]:
				if isinstance(val, basestring) and val.startswith(u'='):
					val = self.python_workspace._eval(val[1:])
				self.experiment.var.set(name, val)
			# Evaluate the run if statement
			if self._break_if is not None and \
				(not first or self.var.break_if_on_first == u'yes'):
				self.python_workspace[u'self'] = self
				if self.python_workspace._eval(self._break_if):
					break
			# Run the item!
			self.experiment.items.execute(self._item)
			# If the repeat_cycle flag was set, run the item again later
			if self.experiment.var.repeat_cycle:
				self.live_dm <<= self.live_dm[self.live_row:self.live_row+1]
				if self.var.order == u'random':
					self.live_dm = self.live_dm[:self.live_row+1] \
						<< operations.shuffle(self.live_dm[self.live_row+1:])
			self.live_row += 1
			first = False
		else:
			# If the loop finished without breaking, it needs to be reset on
			# the next run of the loop item
			self.live_row = None
			self.live_dm = None

	def var_info(self):

		"""See item."""

		return item.item.var_info(self) + [ (colname, safe_decode(col)) \
			for colname, col in self.dm.columns]
Example #9
0
    def from_string(self, string):
        """See item."""

        self.var.clear()
        self.comments = []
        self.reset()
        if string is None:
            return
        for i in string.split(u'\n'):
            self.parse_variable(i)
            cmd, arglist, kwdict = self.syntax.parse_cmd(i)
            if cmd == u'run':
                if len(arglist) != 1 or kwdict:
                    raise osexception(u'Invalid run command: %s' % i)
                self._item = arglist[0]
                continue
            if cmd == u'setcycle':
                if self.ef is not None or self.operations:
                    raise osexception(
                        u'setcycle must come before constraints and operations'
                    )
                if len(arglist) != 3 or kwdict:
                    raise osexception(u'Invalid setcycle command: %s' % i)
                row, var, val = tuple(arglist)
                if row >= len(self.dm):
                    self.dm.length = row + 1
                if var not in self.dm:
                    self.dm[var] = u''
                self.dm[row][var] = val
                continue
            if cmd == u'constrain':
                if self.operations:
                    raise osexception(
                        u'constraints must come before operations')
                if self.ef is None:
                    self.ef = Enforce(self.dm)
                if len(arglist) != 1:
                    raise osexception(u'Invalid constrain command: %s' % i)
                colname = arglist[0]
                try:
                    col = self.dm[colname]
                except:
                    raise osexception(u'Invalid column name: %s' % colname)
                for constraint, value in kwdict.items():
                    try:
                        if constraint == u'maxrep':
                            self.ef.add_constraint(MaxRep,
                                                   cols=[col],
                                                   maxrep=value)
                            continue
                        if constraint == u'mindist':
                            self.ef.add_constraint(MinDist,
                                                   cols=[col],
                                                   mindist=value)
                            continue
                    except InvalidConstraint as e:
                        raise osexception(e)
                    raise osexception(u'Invalid constrain command: %s' % i)
                continue
            if cmd in self.commands:
                self.operations.append((cmd, arglist))
        if len(self.dm) == 0:
            self.dm.length = 1
        if len(self.dm.columns) == 0:
            self.dm.empty_column = u''
        # Backwards compatibility: Older version of OpenSesame can specify the
        # number of cycles through the cycles variable. If the specified number
        # of cycles doesn't match the length of the datamatrix, we change the
        # length of the datamatrix.
        if u'cycles' in self.var and isinstance(self.var.cycles, int) \
          and self.var.cycles != len(self.dm):
            self.dm.length = self.var.cycles
Example #10
0
class loop(item.item):
    """A loop item runs a single other item multiple times"""

    description = u'Repeatedly runs another item'
    valid_orders = u'sequential', u'random'
    commands = [
        u'fullfactorial',
        u'shuffle',
        u'shuffle_horiz',
        u'slice',
        u'sort',
        u'sortby',
        u'reverse',
        u'roll',
        u'weight',
    ]

    def reset(self):
        """See item."""

        self.ef = None
        self.dm = DataMatrix(length=0)
        self.dm.sorted = False
        self.live_dm = None
        self.live_row = None
        self.operations = []
        self._item = u''

        self.var.repeat = 1
        self.var.continuous = u'no'
        self.var.order = u'random'
        self.var.break_if = u'never'
        self.var.break_if_on_first = u'yes'
        self.var.source = u'table'  # file or table
        self.var.source_file = u''

    def from_string(self, string):
        """See item."""

        self.var.clear()
        self.comments = []
        self.reset()
        if string is None:
            return
        for i in string.split(u'\n'):
            self.parse_variable(i)
            cmd, arglist, kwdict = self.syntax.parse_cmd(i)
            if cmd == u'run':
                if len(arglist) != 1 or kwdict:
                    raise osexception(u'Invalid run command: %s' % i)
                self._item = arglist[0]
                continue
            if cmd == u'setcycle':
                if self.ef is not None or self.operations:
                    raise osexception(
                        u'setcycle must come before constraints and operations'
                    )
                if len(arglist) != 3 or kwdict:
                    raise osexception(u'Invalid setcycle command: %s' % i)
                row, var, val = tuple(arglist)
                if row >= len(self.dm):
                    self.dm.length = row + 1
                if var not in self.dm:
                    self.dm[var] = u''
                self.dm[row][var] = val
                continue
            if cmd == u'constrain':
                if self.operations:
                    raise osexception(
                        u'constraints must come before operations')
                if self.ef is None:
                    self.ef = Enforce(self.dm)
                if len(arglist) != 1:
                    raise osexception(u'Invalid constrain command: %s' % i)
                colname = arglist[0]
                try:
                    col = self.dm[colname]
                except:
                    raise osexception(u'Invalid column name: %s' % colname)
                for constraint, value in kwdict.items():
                    try:
                        if constraint == u'maxrep':
                            self.ef.add_constraint(MaxRep,
                                                   cols=[col],
                                                   maxrep=value)
                            continue
                        if constraint == u'mindist':
                            self.ef.add_constraint(MinDist,
                                                   cols=[col],
                                                   mindist=value)
                            continue
                    except InvalidConstraint as e:
                        raise osexception(e)
                    raise osexception(u'Invalid constrain command: %s' % i)
                continue
            if cmd in self.commands:
                self.operations.append((cmd, arglist))
        if len(self.dm) == 0:
            self.dm.length = 1
        if len(self.dm.columns) == 0:
            self.dm.empty_column = u''
        # Backwards compatibility: Older version of OpenSesame can specify the
        # number of cycles through the cycles variable. If the specified number
        # of cycles doesn't match the length of the datamatrix, we change the
        # length of the datamatrix.
        if u'cycles' in self.var and isinstance(self.var.cycles, int) \
          and self.var.cycles != len(self.dm):
            self.dm.length = self.var.cycles

    def to_string(self):
        """See item."""

        # Older versions of OpenSesame use an explicit cycles variable, so we
        # set this here for backwards compatibility.
        self.var.cycles = len(self.dm)
        s = item.item.to_string(self)
        for i, row in enumerate(self.dm):
            for name, val in row:
                s += u'\t%s\n' % \
                 self.syntax.create_cmd(u'setcycle', [i, name, val])
        if self.ef is not None:
            d = {}
            for constraint in self.ef.constraints:
                col = constraint.cols[0]
                if col not in d:
                    d[col] = {}
                if isinstance(constraint, MaxRep):
                    d[col][u'maxrep'] = constraint.maxrep
                elif isinstance(constraint, MinDist):
                    d[col][u'mindist'] = constraint.mindist
            for col, kwdict in d.items():
                s += u'\t%s\n' % self.syntax.create_cmd(
                    u'constrain', [col], kwdict)
        for cmd, arglist in self.operations:
            s += u'\t%s\n' % self.syntax.create_cmd(cmd, arglist)
        s += u'\t%s\n' % self.syntax.create_cmd(u'run', [self._item])
        return s

    def _require_arglist(self, cmd, arglist, minlen=1):
        """
		desc:
			Checks whether a non-empty argument list has been specified, and
			raises an Exception otherwise.

		arguments:
			cmd:
				desc:	The command to check for.
				type:	str
			arglist:
				desc:	The argument list to check.
				type:	list
		"""

        if len(arglist) < minlen:
            raise osexception(u'Invalid argument list for %s' % cmd)

    def _create_live_datamatrix(self):
        """
		desc:
			Builds a live DataMatrix. That is, it takes the orignal DataMatrix
			and applies all the operations as specified.

		returns:
			desc:	A live DataMatrix.
			type:	DataMatrix
		"""

        if self.var.source == u'table':
            src_dm = self.dm
        else:
            from datamatrix import io
            src = self.experiment.pool[self.var.source_file]
            if src.endswith(u'.xlsx'):
                try:
                    src_dm = io.readxlsx(src)
                except Exception as e:
                    raise osexception(u'Failed to read .xlsx file: %s' % src,
                                      exception=e)
            else:
                try:
                    src_dm = io.readtxt(src)
                except Exception as e:
                    raise osexception(
                        u'Failed to read text file (perhaps it has the wrong format or it is not utf-8 encoded): %s'
                        % src,
                        exception=e)
        for column_name in src_dm.column_names:
            if not self.syntax.valid_var_name(column_name):
                raise osexception(
                 u'The loop table contains an invalid column name: 'u'\'%s\'' \
                 % column_name)
        # The number of repeats should be numeric. If not, then give an error.
        # This can also occur when generating a preview of a loop table if
        # repeat is variable.
        if not isinstance(self.var.repeat, (int, float)):
            raise osexception(
             u'Don\'t know how to generate a DataMatrix for "%s" repeats' \
             % self.var.repeat)
        length = int(len(src_dm) * self.var.repeat)
        dm = DataMatrix(length=0)
        while len(dm) < length:
            i = min(length - len(dm), len(src_dm))
            if self.var.order == u'random':
                dm <<= operations.shuffle(src_dm)[:i]
            else:
                dm <<= src_dm[:i]
        if self.var.order == u'random':
            dm = operations.shuffle(dm)
        if self.ef is not None:
            self.ef.dm = dm
            dm = self.ef.enforce()
        for cmd, arglist in self.operations:
            # The column name is always specified last, or not at all
            if arglist:
                try:
                    colname = arglist[-1]
                    col = dm[colname]
                except:
                    raise osexception(u'Column %s does not exist' %
                                      arglist[-1])
            if cmd == u'fullfactorial':
                dm = operations.fullfactorial(dm)
            elif cmd == u'shuffle':
                if not arglist:
                    dm = operations.shuffle(dm)
                else:
                    dm[colname] = operations.shuffle(col)
            elif cmd == u'shuffle_horiz':
                if not arglist:
                    dm = operations.shuffle_horiz(dm)
                else:
                    dm = operations.shuffle_horiz(
                        *[dm[_colname] for _colname in arglist])
            elif cmd == u'slice':
                self._require_arglist(cmd, arglist, minlen=2)
                dm = dm[arglist[0]:arglist[1]]
            elif cmd == u'sort':
                self._require_arglist(cmd, arglist)
                dm[colname] = operations.sort(col)
            elif cmd == u'sortby':
                self._require_arglist(cmd, arglist)
                dm = operations.sort(dm, by=col)
            elif cmd == u'reverse':
                if not arglist:
                    dm = dm[::-1]
                else:
                    dm[colname] = col[::-1]
            elif cmd == u'roll':
                self._require_arglist(cmd, arglist)
                steps = arglist[0]
                if not isinstance(steps, int):
                    raise osexception(u'roll steps should be numeric')
                if len(arglist) == 1:
                    dm = dm[-steps:] << dm[:-steps]
                else:
                    dm[colname] = list(col[-steps:]) + list(col[:-steps])
            elif cmd == u'weight':
                self._require_arglist(cmd, arglist)
                dm = operations.weight(col)
        return dm

    def prepare(self):
        """See item."""

        item.item.prepare(self)
        # Deprecation errors. The direct reference to __vars__ prevents a lookup
        # in the parent var store.
        if (u'skip' in self.var.__vars__ and self.var.skip != 0) \
         or (u'offset' in self.var.__vars__ and self.var.offset != u'no'):
            raise osexception(
                u'The skip and offset options have been removed. Please refer to the documentation of the loop item for more information.'
            )
        # Compile break-if statement
        break_if = self.var.get(u'break_if', _eval=False)
        if break_if not in (u'never', u''):
            self._break_if = self.syntax.compile_cond(break_if)
        else:
            self._break_if = None
        # Create a keyboard to flush responses between cycles
        self._keyboard = openexp.keyboard.keyboard(self.experiment)
        # Make sure the item to run exists
        if self._item not in self.experiment.items:
            raise osexception(
             u"Could not find item '%s', which is called by loop item '%s'" \
             % (self._item, self.name))

    def run(self):
        """See item."""

        self.set_item_onset()
        if self.live_dm is None or self.var.continuous == u'no':
            self.live_dm = self._create_live_datamatrix()
            self.live_row = 0
        first = True
        while self.live_row < len(self.live_dm):
            self.experiment.var.repeat_cycle = 0
            self.experiment.var.live_row = self.live_row
            self.experiment.var.set('live_row_%s' % self.name, self.live_row)
            for name, val in self.live_dm[self.live_row]:
                if isinstance(val, basestring) and val.startswith(u'='):
                    val = self.python_workspace._eval(val[1:])
                self.experiment.var.set(name, val)
            # Evaluate the run if statement
            if self._break_if is not None and \
             (not first or self.var.break_if_on_first == u'yes'):
                self.python_workspace[u'self'] = self
                if self.python_workspace._eval(self._break_if):
                    break
            # Run the item!
            self.experiment.items.execute(self._item)
            # If the repeat_cycle flag was set, run the item again later
            if self.experiment.var.repeat_cycle:
                self.live_dm <<= self.live_dm[self.live_row:self.live_row + 1]
                if self.var.order == u'random':
                    self.live_dm = self.live_dm[:self.live_row+1] \
                     << operations.shuffle(self.live_dm[self.live_row+1:])
            self.live_row += 1
            first = False
        else:
            # If the loop finished without breaking, it needs to be reset on
            # the next run of the loop item
            self.live_row = None
            self.live_dm = None

    def var_info(self):
        """See item."""

        return item.item.var_info(self) + [ (colname, safe_decode(col)) \
         for colname, col in self.dm.columns]
Example #11
0
from datamatrix import io
from pseudorandom import Enforce, MaxRep, MinDist

# Read a datafile with Pandas and convert it to a pseudorandom DataFrame.
dm = io.readtxt('examples/data.csv')
print(dm)
# Create an Enforce object, and add two constraints
ef = Enforce(dm)
ef.add_constraint(MaxRep, cols=[dm.category], maxrep=1)
ef.add_constraint(MinDist, cols=[dm.word], mindist=4)
# Enforce the constraints
dm = ef.enforce()
# See the resulting DataFrame and a report of how long the enforcement took.
print(dm)
print(ef.report)
Example #12
0
	def from_string(self, string):

		"""See item."""

		self.variables = {}
		self.comments = []
		self.reset()
		if string is None:
			return
		for i in string.split(u'\n'):
			self.parse_variable(i)
			cmd, arglist, kwdict = self.syntax.parse_cmd(i)
			if cmd == u'run':
				if len(arglist) != 1 or kwdict:
					raise osexception(u'Invalid run command: %s' % i)
				self._item = arglist[0]
				continue
			if cmd == u'setcycle':
				if self.ef is not None or self.operations:
					raise osexception(
						u'setcycle must come before constraints and operations')
				if len(arglist) != 3 or kwdict:
					raise osexception(u'Invalid setcycle command: %s' % i)
				row, var, val = tuple(arglist)
				if row >= len(self.dm):
					self.dm.length = row+1
				if var not in self.dm:
					self.dm[var] = u''
				self.dm[row][var] = val
				continue
			if cmd == u'constrain':
				if self.operations:
					raise osexception(
						u'constraints must come before operations')
				if self.ef is None:
					self.ef = Enforce(self.dm)
				if len(arglist) != 1:
					raise osexception(u'Invalid constrain command: %s' % i)
				colname = arglist[0]
				try:
					col = self.dm[colname]
				except:
					raise osexception(u'Invalid column name: %s' % colname)
				for constraint, value in kwdict.items():
					try:
						if constraint == u'maxrep':
							self.ef.add_constraint(MaxRep, cols=[col],
								maxrep=value)
							continue
						if constraint == u'mindist':
							self.ef.add_constraint(MinDist, cols=[col],
								mindist=value)
							continue
					except InvalidConstraint as e:
						raise osexception(e)
					raise osexception(u'Invalid constrain command: %s' % i)
				continue
			if cmd in self.commands:
				self.operations.append( (cmd, arglist) )
		if len(self.dm) == 0:
			self.dm.length = 1
		if len(self.dm.columns) == 0:
			self.dm.empty_column = u''
Example #13
0
    def from_string(self, string):
        """See item."""

        self.var.clear()
        self.comments = []
        self.reset()
        if string is None:
            return
        for i in string.split(u'\n'):
            self.parse_variable(i)
            cmd, arglist, kwdict = self.syntax.parse_cmd(i)
            if cmd == u'run':
                if len(arglist) != 1 or kwdict:
                    raise osexception(u'Invalid run command: %s' % i)
                self._item = arglist[0]
                continue
            if cmd == u'setcycle':
                if self.ef is not None or self.operations:
                    raise osexception(
                        u'setcycle must come before constraints and operations'
                    )
                if len(arglist) != 3 or kwdict:
                    raise osexception(u'Invalid setcycle command: %s' % i)
                row, var, val = tuple(arglist)
                if row >= len(self.dm):
                    self.dm.length = row + 1
                if var not in self.dm:
                    self.dm[var] = u''
                self.dm[row][var] = val
                continue
            if cmd == u'constrain':
                if self.operations:
                    raise osexception(
                        u'constraints must come before operations')
                if self.ef is None:
                    self.ef = Enforce(self.dm)
                if len(arglist) != 1:
                    raise osexception(u'Invalid constrain command: %s' % i)
                colname = arglist[0]
                try:
                    col = self.dm[colname]
                except:
                    raise osexception(u'Invalid column name: %s' % colname)
                for constraint, value in kwdict.items():
                    try:
                        if constraint == u'maxrep':
                            self.ef.add_constraint(MaxRep,
                                                   cols=[col],
                                                   maxrep=value)
                            continue
                        if constraint == u'mindist':
                            self.ef.add_constraint(MinDist,
                                                   cols=[col],
                                                   mindist=value)
                            continue
                    except InvalidConstraint as e:
                        raise osexception(e)
                    raise osexception(u'Invalid constrain command: %s' % i)
                continue
            if cmd in self.commands:
                self.operations.append((cmd, arglist))
        if len(self.dm) == 0:
            self.dm.length = 1
        if len(self.dm.columns) == 0:
            self.dm.empty_column = u''
Example #14
0
    def _create_live_datamatrix(self):
        """
		desc:
			Builds a live DataMatrix. That is, it takes the orignal DataMatrix
			and applies all the operations as specified.

		returns:
			desc:	A live DataMatrix.
			type:	DataMatrix
		"""

        src_dm = self.dm if self.var.source == u'table' else self._read_file()
        for column_name in src_dm.column_names:
            if not self.syntax.valid_var_name(column_name):
                raise osexception(
                    u'The loop table contains an invalid column name: '
                    u'\'%s\'' % column_name)
        # The number of repeats should be numeric. If not, then give an error.
        # This can also occur when generating a preview of a loop table if
        # repeat is variable.
        if not isinstance(self.var.repeat, (int, float)):
            raise osexception(
                u'Don\'t know how to generate a DataMatrix for "%s" repeats' %
                self.var.repeat)
        length = int(len(src_dm) * self.var.repeat)
        dm = DataMatrix(length=0)
        while len(dm) < length:
            i = min(length - len(dm), len(src_dm))
            if self.var.order == u'random':
                dm <<= operations.shuffle(src_dm)[:i]
            else:
                dm <<= src_dm[:i]
        if self.var.order == u'random':
            dm = operations.shuffle(dm)
        # Constraints come before loop operations
        if self._constraints:
            self.ef = Enforce(dm)
            for constraint_cls, colname, kwargs in self._constraints:
                self.ef.add_constraint(constraint_cls,
                                       cols=dm[colname],
                                       **kwargs)
            dm = self.ef.enforce()
        # Operations come last
        for cmd, arglist in self._operations:
            # The column name is always specified last, or not at all
            if arglist:
                try:
                    colname = arglist[-1]
                    col = dm[colname]
                except:
                    raise osexception(u'Column %s does not exist' %
                                      arglist[-1])
            if cmd == u'fullfactorial':
                dm = operations.fullfactorial(dm)
            elif cmd == u'shuffle':
                if not arglist:
                    dm = operations.shuffle(dm)
                else:
                    dm[colname] = operations.shuffle(col)
            elif cmd == u'shuffle_horiz':
                if not arglist:
                    dm = operations.shuffle_horiz(dm)
                else:
                    # There can be multiple column names, so we need to check
                    # if all of them exist, rather than only the last one as
                    # we did above.
                    for _colname in arglist:
                        try:
                            dm[_colname]
                        except:
                            raise osexception(u'Column %s does not exist' %
                                              _colname)
                    dm = operations.shuffle_horiz(
                        *[dm[_colname] for _colname in arglist])
            elif cmd == u'slice':
                self._require_arglist(cmd, arglist, minlen=2)
                dm = dm[arglist[0]:arglist[1]]
            elif cmd == u'sort':
                self._require_arglist(cmd, arglist)
                dm[colname] = operations.sort(col)
            elif cmd == u'sortby':
                self._require_arglist(cmd, arglist)
                dm = operations.sort(dm, by=col)
            elif cmd == u'reverse':
                if not arglist:
                    dm = dm[::-1]
                else:
                    dm[colname] = col[::-1]
            elif cmd == u'roll':
                self._require_arglist(cmd, arglist)
                steps = arglist[0]
                if not isinstance(steps, int):
                    raise osexception(u'roll steps should be numeric')
                if len(arglist) == 1:
                    dm = dm[-steps:] << dm[:-steps]
                else:
                    dm[colname] = list(col[-steps:]) + list(col[:-steps])
            elif cmd == u'weight':
                self._require_arglist(cmd, arglist)
                try:
                    dm = operations.weight(col)
                except TypeError:
                    raise osexception(
                        u'weight values should be non-negative numeric values')
        return dm