jakdot/pyactr

Output a variable within a production?

Closed this issue · 6 comments

How do I output a variable within a production?

In ACT-R, I can do this:

!output!	(The result is =sum)

I found Buffer.show which lets me output a specific slot, but I can't find anything that does the equivalent of the ACT-R !output! for variables (and arbitrary text).

Thank you!

Sorry, forgot to respond. Indeed, you cannot print arbitrary texts in productions if you write production rules using productionstring. However, you can do this if you create the rule without productionstring.

Basically, all productions are underlyingly generator functions and productionstring converts a string into a generator function. If you write a production directly as a generator function, you can print whatever you like, since you stepped outside of pyactr and work directly with Python.

I am putting an example here from the tutorial u1_count.py. Instead of specifying the rule increment using productionstring, you could create a generator function as shown below, and then you can put a Python code inside that generator function and it will be triggered when the rule is selected.

def incrementrule():
    """
    This rule specifies increment rule without using the string, in contrast to the example in tutorials.
    We can specify an arbitrary print statement after the rule is triggered (after the first yield).
    """
    yield {"=g": actr.makechunk("", "countFrom", count="=x", end="~=x"), "=retrieval": actr.makechunk("", "countOrder", first="=x", second="=y")}
    # The following prints the value of x, which is bound to the value of first in retrieval
    print("The value of 'x' is ", counting.retrieval.pop().first) 
    yield {"=g": actr.makechunk("", "countFrom", count="=y"), "+retrieval": actr.makechunk("", "countOrder", first="=y")}

# When you do this you have to specify that the increment rule created above should be part of productions
# When we use productionstring, this does not need to be done, the rule is inserted automatically.
counting.productions.update({"increment2": {"rule": incrementrule, "utility": 0, "reward": None}})

Thank you for the response and example!

Part of my goal is making things easier for people to read & understand, so this solution is kind of sub-optimal for my purposes.

I had some success with straight text by monkey patching Buffer like this:

def print_text(*args):
    text = ''.join(args[1:])
    text = text.strip("'")
    print(text)

Buffer.print_text = print_text

and then using it in the production like this:

	!goal>
		print_text 'Yes'

But there are a couple of problems with this approach.

  • I haven't figured out to how to lookup & print variables from the production
  • Trying to print output using variables/slots from two different buffers might be a problem

Do you think this approach (or something like it) can be made to work? It would be a lot easier to read & understand.

(For a larger view of what I'm trying to do - I'm generating code for pyactr, ccm suite (also Python), and ACT-R (Lisp) from a common format. So I'm trying to maintain functional parity as well as making the code readable and easy to compare with the other frameworks.)

This is an interesting project. It would be very useful to have that. I have a PhD student who was thinking about something similar - he wanted to compare Lisp ACT-R and pyactr. I'll let him know about your work. I am curious to hear how you proceed, please keep me updated, you can find my e-mail on my website.

Re printing: if you just need to print some text, you can also do that directly in pyactr. The command show prints the value of a slot but if you give it some text that does not correspond to the name of any slot, it prints just that text. For example, the following prints hello when increment is fired:

counting.productionstring(name="increment", string="""
    =g>
    isa     countFrom
    count       =x
    end         ~=x
    =retrieval>
    isa     countOrder
    first       =x
    second      =y
    ==>
    !g>
    show hello
    =g>
    isa     countFrom
    count       =y
    +retrieval>
    isa     countOrder
    first       =y""")

Printing values of ACT-R variables cannot be done directly, unfortunately. However, you could always print the value of the slot to which the variable is bound. So, for example, in the case above if you need to get the value of y, you just specify show second for retrieval. This strategy should work for any ACT-R variable, since they are always bound to some slot. Would this be enough? Otherwise, getting to an ACT-R variable in pyactr directly is pretty tricky, I am afraid, because these are hidden away in one of the modules.

he wanted to compare Lisp ACT-R and pyactr.

Yes - that's another part of my motivation - comparing the different implementations. (In fact as I was developing gactar I directly compared my generated pyactr code with my generated lisp code to ensure my productions were correct.) I've started by implementing the basics of ACT-R in my language and since it forces the user to use this restricted set of commands, I'm hoping it will raise questions - i.e. I can do this in X, but I can't do it in Y - should I be able to? So it can raise technical questions to a more theoretical level.

Obviously what I have is just a starting point, but I wanted to get it out there to see if there's any interest and hopefully foster discussion.

I had seen show, but it is restricted to outputting one slot. I don't think I can show "The result is" second, can I? Maybe I can see how show is getting the slot contents and use that in my monkey patch to improve it though.

I think mapping vars back to slots could work with some more internal bookkeeping on my side.

Printing a string from two different buffers can't be addressed by this. e.g. The =x is =y where they come from two different buffers. Maybe that's just a restriction I'll have to impose.

Thank you for the feedback!

I figured out a way to do this using some funky monkey patches.

If anyone needs to do this in the future, you can see what I did in this PR.

The python code in pyactr.go is output to the generated file, then I use print_text in productions like this:

!goal>
	print_text "' The start is ', goal.start, ' and the second is ', 'retrieval.second'"
  • it handles slots from multiple buffers (as seen above)
  • limitation: you can only print_text once per production

(I arrived back here while looking at something related and thought I would update for anyone who runs across this.)

Since I commented here, I've moved that python code out of pyactr.go into its own file. If you want to use this, you should be able to just add pyactr_print.py to your project and then:

import pyactr_print

and use it in your productions:

!goal>
	print_text "'Start is ', goal.start, ' and second is ', 'retrieval.second'"

@jakdot If you would be interested in including this in pyactr, I would be happy to integrate it and create a pull request.