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

Output a variable within a production? #8

Closed
asmaloney opened this issue Sep 5, 2021 · 6 comments
Closed

Output a variable within a production? #8

asmaloney opened this issue Sep 5, 2021 · 6 comments

Comments

@asmaloney
Copy link
Contributor

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!

@jakdot
Copy link
Owner

jakdot commented Oct 4, 2021

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}})

@asmaloney
Copy link
Contributor Author

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.)

@jakdot
Copy link
Owner

jakdot commented Oct 4, 2021

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.

@asmaloney
Copy link
Contributor Author

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!

@asmaloney
Copy link
Contributor Author

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

@asmaloney
Copy link
Contributor Author

(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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants