-
Notifications
You must be signed in to change notification settings - Fork 20
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
Support and testing for equality-atom and if-then-else based RDDL-style boolean interoperability #120
base: devel
Are you sure you want to change the base?
Conversation
…aoch for boolean interop
…ort equality-atom based boolean interop development Tests are focused on supporting Boolean/numeric interoperability for RDDL as discussed in aig-upf#91. Currently passing all relevent tests. Read-side IO is untested. Write-side IO does not test necessary simplifications from the equality-atom, if-then-else wrapper, and sumterm-based quantifier replacements to typical RDDL patterns.
…ls rather than 1/0 in written files for bool vars
Hi @mejrpete,
That is okay, will check, but the problem is that I don't really have very strong "constraints" on this. Basically because I haven't any planners using the RDDL -> AST features.
The "significant onus on the user" is a bit fuzzy for me, I need to see specific examples where this becomes an issue. Python being Python (and not C++) it is not clear to me what degree of "automation" is possible, analogous to what would be the approach in C++ (e.g. generic programming, traits, concepts, etc.) to essentially implement what is code generation. Overloading operators in Python was a massive pain in the bum and the best we could come up with were the frankly, quite bothersome, concept of If you could come with a concrete example that illustrates this issue - on a Regards, Miquel. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @mejrpete,
it is looking good other than the comment regarding the ValueError
being raised without data. Get back to me and I am happy to approve.
@@ -610,7 +704,9 @@ def rewrite(self, expr): | |||
if len(re_st) > 0: | |||
# MRJ: Random variables need parenthesis, other functions need | |||
# brackets... | |||
if expr.symbol.symbol in builtins.get_random_binary_functions(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, this one was a bit of a puzzle back in the day. Nice improvement.
@@ -14,6 +14,14 @@ def normal(mu, sigma): | |||
return np.random.normal(mu, sigma) | |||
return normal_func(mu, sigma) | |||
|
|||
def bernoulli(p): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cheers for adding the "master" distribution
@@ -29,7 +29,7 @@ def __hash__(self): | |||
return hash(self.name) | |||
|
|||
def __eq__(self, other): | |||
return self.name == other.name | |||
return self.name == other.name and self.language == other.language |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice one
@@ -191,25 +191,14 @@ def children(s: Sort) -> Set[Sort]: | |||
|
|||
def int_encode_fn(x): | |||
if isinstance(x, float) and not x.is_integer(): | |||
raise ValueError(x) # We don't want 1.2 to get encoded as an int | |||
raise ValueError() # We don't want 1.2 to get encoded as an int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @mejrpete,
is there any reason for raising the exception with no data?
I have been thinking a bit about the issues regarding modelling support @mejrpete you mentioned earlier. Thinking a bit about it I see the following as possible "helpers":
This would be a global function much like def lt(t: CompoundTerm, v: Constant) -> Atom:
if t.symbol.builtin:
raise SyntaxError("Cannot define literals for built-in symbol: {}".format(str(t.symbol)))
return t == v So one could write:
This would require to overwrite a binary operator like
What do you think? any other ideas? |
Note that this PR is submitted as a draft, since the true PR should probably be opened against a separate feature branch, rather than
devel
.Boolean interop for RDDL requires the ability to move from
Term -> Formula
and fromFormula -> Term
within the AST built inTarski
as discussed in #91. In general, we need the ability to construct nested formulae which include both logical operations on expressions with atoms based on the numerically-derivedBoolean
sort, and direct mathematical operations on these items.To accomplish this in practice, two patterns can be used.
1)
Term -> Formula
:Given a
Term
with sortBoolean
, we can use the typical equality predicate and a constantTrue/1
value to wrap to aFormula
(for a language which has both theBoolean
andEquality
theories attached)2)
Formula -> Term
:Given a
Formula
, we can wrap out to aTerm
with sortBoolean
using theIfThenElse
term, and corresponding boolean constants.With these two patterns, we can arbitrarily move back and forth between
Formula
andTerm
objects depending on the operations needed when building the AST inTarski
. Additionally, because these patterns are easily identifiable with simple pattern matching, when performing write-side IO for an RDDL domain which includes these wrappers within theTarski
representation, we can remove the wrappers accordingly, and generate legal RDDL which does not include the additional representational overhead involved with the wrapper patterns discussed above.The
Tarski
modeling approach for RDDL using these patterns involves representing Boolean RDDL values asBoolean
(sort) codomain functions, with equality-based atoms as the basic logical type (e.g.(a == lang.constant(1, lang.Boolean)
). This is in contrast to the approach explored in therddl-support
branch (#96), which assumed the use ofPredicate
for representation of Boolean RDDL values. One additional advantage of this change is that while the closed-world assumption prohibits the declaration offalse
-valuednon-fluent
s and initial-statestate-fluent
s (necessary in RDDL, since Boolean valued fluents can be declared with default values oftrue
) in an implementation based onPredicate
representations for these fluents (as in #96), the use ofBoolean
codomain functions in this alternate approach trivially avoids this issue.Included changes/additions:
Boolean
sort descended fromNatural
when the Boolean theory is attached to aFirstOrderLanguage
Bernoulli
as a builtin for theRandom
theoryFormula
andTerm
items when using theBoolean
sort as described.true
andfalse
literals rather than0/1
when writing RDDL from our representation (needed as far as I know for appropriate behavior with both thePROST
andrddlsim
RDDL parsers)Boolean
theory changesOmissions/Necessary future discussion
Tarski
as a tool to directly construct the AST and domain/instance models for RDDL-specified problems) requiring the explicit use of these wrappers puts significant onus on the user to understand the distinction betweenFormula
andTerm
withinTarski
in order to appropriately use these wrappers and correctly construct their domain/instance. While this is potentially workable, it would be preferable from the user's perspective to have some level of "automatic" injection of wrappers in instances where there is a mismatch between input to an operator and the expected object type (e.g. when dispatching alor
called from aFormula
where the argument is aTerm
with sortBoolean
). This would involve building recovery logic with knowledge of theBoolean
sort intoTarski
's operator dispatching implementation, which may or may not be acceptable from a design standpoint. This may be a worthwhile point of discussion!