BackwardChainer: working with stricter assumption from AtomSpace#241
BackwardChainer: working with stricter assumption from AtomSpace#241williampma merged 12 commits intoopencog:masterfrom
Conversation
- when there's no free vars in the atom
- Need to always allow non-var VariableNode to match to any-name VariableNode
- Since BC should be working on the assumption that each VariableNode has one unique scope (ie. the same VariableNode should never appear on more than one scope)
- select rule first before unification, and save the unification result
BackwardChainer: working with stricter assumption from AtomSpace
|
OK, so .. treating two different atoms as the same, because they differ only by a re-labelling of variables is called "alpha conversion". Ben started talking about this here: opencog/opencog#1742 I recommend that alpha-conversion be done using some sort of independent data-scrubber run periodically, or maybe run when an atom is added to the atomspace; it should be an independent unit of code, applying some data-scrubbing rules. It should not be hard-coded into the guts of things, as that will cause other troubles. Also: you can only do alpha-conversion only if there are no conflicts with other variables. So, for example, you can convert |
|
OK, so .. treating two different atoms as the same, because they differ
My suggestion is that alpha-conversion be done whenever a variable-scoping
|
|
You may "intellectually" consider that each scoped variable node is unique, but nothing currently prevents users from using the same variable name in different scoped expressions. so e.g. people could write: We can solve this second problem like so: Make every scoped link derive from LambdaLink. Currently, when you construct a LambdaLink, it runs some C++ code to figure ouot where the variables are, what the body is; some general pre-processing, so that later users can get fast access to the variables. What we could do in this C++ code is to create new, unique (random) variable names, go through the body, and paste those in. Now, you cannot accidentally bind the same variable in two different expressions. This raises two issues: (2) There's a practical problem: whoever is creating the LambdaLink might get tripped up by the changes. I can't think of a convincing example right now, but these come up in the code, where old code assumes that A is done before B but newer code has to twiddle the contens of B that A doesn't know about, and it can be messy/tricky/confusing. I'm hitting an issue like that right now, with DefineLink, where I need to use variables before I know what they are. Yuck. Solvable, but a time-sink. |
true. But the conclusion William and I came to, after significant
Right. We should not let this happen.
Basically, at the time a new scoping link is created, we want to both -- enforce alpha-equivalence -- enforce the rule that each variable is bound only by a single scoping If the easiest way to do this involves making all the scoping links inherit (2) There's a practical problem: whoever is creating the LambdaLink might
Hmmm.... One could make a function "get_new_variable_name" that gets a new -- first call get_new_variable_name to get new, unique variable names for -- then create the scoping link that scopes these variable names This avoids ever changing the name of a VariableNode. But I'm not sure if Anyway, it seems we need to -- enforce alpha-equivalence -- enforce the rule that each variable is bound only by a single scoping to make backward chaining work in a non-horrible way...
|
|
OK. I guess you can do the alpha-equivalence check when an atom is added to the atomspace. You'd have to run the pattern matcher, see if there already is some other alpha-equivalent lambda-link in the atomspace, and return that. The only problem I see is a stupid but nasty shared-library problem, where the atomspace needs the pattern matcher needs the atomspace etc. and CMake pukes on this. It has been very very painful. As far as I can tell, CMake enforces this because its a MS Windows limitation, since I think it would work OK with glibc. So far, I've been able to hack around this, but its ... fragile. |
|
Hi, On Sat, Aug 29, 2015 at 1:26 AM, bgoertzel notifications@github.com wrote:
Maybe we could do what Lisp does for macros, where (gensym) is used to CassioCassio Pennachin |
|
Guys, maybe I'm missing something here, but if all variables are scoped (and I mean explicitly scoped, not like today). Then no need to constraint the user to have different variable names. Really it's a hard constraint to satisfy, you can't ask user A and user B working on 2 separate component of the same system not to use the same variable names. Neither can you have the user enter that and have opencog rename that behind his back and display the next time he wants to look it up. I understand that, as William pointed out, you cannot know in advance whether a variable is gonna be scoped of not (cause before adding the scope link you need to add the variable). So let's make everything variable use explicitly scoped, if it's not then, it's got to be a global one, cause there would be no such thing as a local unscoped variable. Alpha-equivalence should be short live and transparently performed for the user, the result of an alpha-convertion should only live in the RAM of the execution mechanism, not in the AtomSpace. Do you agree? |
|
Nil, I agree, except that "explicit scoping" has nothing to do with it. The problem is that the scoping is not being honored. If some variable is implicitly scoped, and the code does not respect that, and gloms it anyway, then, yes, bad things will happen. Its exactly the same bad things that would happen if it was explicitly scoped. So, the answer is if a variable is scoped, explicitly or implicitly, then code should honor that scoping! Examples where scoping is not being honored would make this conversation easier, though. I'm guessing that these show up in the chainers, and they might be hard to deal with. I can't tell from here. |
|
I am nervous coming up with an example, since I am still kind of confused. Here is one perhaps: Given applying evaluation-to-member-rule can give (assuming variables naming are not unique yet) Now, applying evaluation-to-member-rule again, it will find the EvaluationLink inside these scoped one and produce One of the $X in the internal EvaluationLink is actually from a different scope, but we cannot tell. With different variable naming we see ie. this new MemberLink is actually living inside the scope of from within the top-most MemberLink. |
|
Sorry, messed up the example. Fixed (I think). |
|
William -- yeah, without unique variable names in the Atomspace, what needs to be done Or, enforcing uniqueness of variable names in the Atomspace avoids the ben On Tue, Sep 1, 2015 at 11:08 AM, William Ma notifications@github.com
Ben Goertzel, PhD "The reasonable man adapts himself to the world: the unreasonable one |
|
All that Nil was saying is that, for the second rule application, you cannot use $X again; you must not paste such a thing into the inside of an expression where $X is already bound (implicitly or explicitly). Basically, when you are converting an expression using a rule of some kind, you must scan the entire expression for variables, and then pick a variable name that does not yet occur inside that expression. |
|
That's not the only problem. Even with new var name as I did at the end with You cannot find out where $X is scoped because $X is under two different scope. So the original creation of them need to have different name. Also, with rule's output using new var name, it will create a bunch of same atom There are other ideas that I have floating in my mind (like never allow PM to match to stuff already inside a scope, or make the output somehow insert the new MemberLink into the other scoping link...), but I think the restriction mentioned at the top seems cleanest. |
|
Hmm. I guess I don't understand the problem. When you write the $X is clearly free, since its not explicitly scoped, and there is nowhere where it can be implicitly scoped. What's wrong with $X being free? It seems to say "birds belong to the set of things that get eaten", and that seems like a valid statement that can be assigned a plausible TV. But I'm just saying things; I don't quite understand the "big picture" that is being pursued here. If you want to scope both at the same time, you could stack one on the other, or as Nil suggested, do them in "parallel": either way, stacking them, or doing them in "parallel" would require some new kind of rule. |
|
You could even convert the "parallel" case to a stacked case using two different curry rules (for two different orders) and converted the stacked version to a parallel version with an uncurry rule. |
changes to make BC works with some strict assumption (not yet satisfied) from the AtomSpace
(SatisfyingSetLink (VariableNode $X) (humans eat $X)),(SatisfyingSetLink (VariableNode $Y) (humans eat $Y))are treated as exactly the same atomThis allows me to remove hacky codes that were causing all sort of contradictions in terms of VariableNode, allows me to cleanly unify to the output of rules like member-to-evaluation-rule, etc. In general, BC probably won't work if the above 2 points are not satisfied.
However, there're still a little bit of work-around codes left. eg. right now all the rules' BindLink share same named VariableNode everywhere; PM will still return
(SatisfyingSetLink (VariableNode $X) (humans eat $X)),(SatisfyingSetLink (VariableNode $Y) (humans eat $Y))as separate results.FC will also needed to be changed so that rules like https://github.com/opencog/opencog/blob/master/opencog/reasoning/pln/rules/evaluation-to-member-rule.scm will output unique VariableNode inside the output SatisfyingSetLink between steps.
optimize rule selection + unification bases on recent discussion
and other stuff