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

Substitution lazy cloning #270

Merged
merged 4 commits into from
Oct 7, 2020
Merged

Conversation

AntoniusW
Copy link
Collaborator

Removes the need for upfront cloning of Substitutions when checking unification.

- Add Substitution.unify that clones the partial input substitution on
  demand (i.e., when an additional variable assignment is required).
- Modify NaiveGrounder and AbstractLiteralInstantiationStrategy to not
  clone Substitutions upfront.
@codecov
Copy link

codecov bot commented Oct 1, 2020

Codecov Report

❗ No coverage uploaded for pull request base (master@7788331). Click here to learn what that means.
The diff coverage is 84.61%.

Impacted file tree graph

@@            Coverage Diff            @@
##             master     #270   +/-   ##
=========================================
  Coverage          ?   77.44%           
  Complexity        ?     2311           
=========================================
  Files             ?      178           
  Lines             ?     7782           
  Branches          ?     1352           
=========================================
  Hits              ?     6027           
  Misses            ?     1335           
  Partials          ?      420           
Impacted Files Coverage Δ Complexity Δ
...a/at/ac/tuwien/kr/alpha/grounder/Substitution.java 77.77% <83.33%> (ø) 18.00 <2.00> (?)
.../at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java 86.12% <100.00%> (ø) 69.00 <0.00> (?)
...ntiation/AbstractLiteralInstantiationStrategy.java 100.00% <100.00%> (ø) 8.00 <0.00> (?)
.../grounder/transformation/StratifiedEvaluation.java 97.79% <100.00%> (ø) 53.00 <0.00> (?)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7788331...af66c5a. Read the comment docs.

Copy link
Collaborator

@madmike200590 madmike200590 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think everything is ok from a functional point of view, i.e. the code does what it's supposed to do, however, I find it hard to read and confusing in places, see more detailed comments. I'm aware not all of those are easy to address (or even necessarily need to be addressed), but I think some discussion makes sense.

}
return substitution;

boolean unifyTerms(Term termNonGround, Term termGround, Substitution partialSubstitution) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems wrong (or at least really confusing) to me that we now have UnificationHelper::unifyTerms(Term, Term, Substitution) as well as Substitution::unifyTerms(Term, Term). The only difference seems to be that the one in Substitution always operates on this and modifying the substitution it's called on, while the other one operates on the given partialSubstitution. I think the version in UnificationHelper should be adapted so that the other one isn't needed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, the Substitution::unifyTerms(Term, Term) is only ever needed in unit tests, while the new one is not tested. Will adapt the tests to run on the real thing and get rid of the other.

* already is a unifier, it is returned. If the unifying substitution is an extension of the input
* substitution, a new substitution will be returned.
*/
public static Substitution unify(Atom atom, Instance instance, Substitution substitution) {
Copy link
Collaborator

@madmike200590 madmike200590 Oct 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More of a general question, and maybe out of the scope of this review... We also have classes Unifier and Unification, with Unification implementing utility functions looking very similar to some of the methods in Substitution. In order to avoid duplicating code, or writing several very similar versions, would it make sense to use the (accordingly adapted) methods from Unification here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a slight difference between the two (and maybe we should adapt our naming): the one in Substitution is assuming that variables only ever occur on the left-hand side (i.e., the second parameter is always ground). The version in Unification really computes the unifier between two non-ground Atoms. Maybe we should rename the methods in Substitution to instantiate() instead of unify().

Copy link
Collaborator Author

@AntoniusW AntoniusW Oct 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to rename Substitution::unify to Substitution::specializeSubstitution as it specializes the given partial substitution to also unify the given atom and the instance.

private static class UnificationHelper {
Substitution returnSubstitution;

Substitution unify(List<Term> termList, Instance instance, Substitution partialSubstitution) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this is a matter of personal taste, but I'm not sure I agree with the return types here..
I think it might make sense if unifyTerms returned something other than boolean, thereby getting rid of the side-effects of potentially modifying the given partialSubstitution. Instead, unifyTerms could return a Substitution that may be either a new (cloned) one, or the one passed in in case nothing changed. Alternatively, we could also use an Optional<Substitution> here, where the presence of a value would indicate that a new variable got bound. The version with Optional could also be used for Substitution::unify as an alternative to returning null.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Letting unifyTerms return a Substitution is a good idea. I would not want to return an Optional however, since substitutions are used at the core of grounding and Optional is likely slower and creates more objects.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I decided to remove unifyTerms as it was only ever used in unit tests. These unit tests have been adapted to now test the new/renamed specializeSubstitution.

@AntoniusW
Copy link
Collaborator Author

I think everything is ok from a functional point of view, i.e. the code does what it's supposed to do, however, I find it hard to read and confusing in places, see more detailed comments. I'm aware not all of those are easy to address (or even necessarily need to be addressed), but I think some discussion makes sense.

That is exactly the reason why I like our four-eyes policy on pull requests. It forces one to deliver better code. Thank you for the comments!

- Remove superfluous Substitution.unifyTerms.
- Adapt SubstitutionTest to Substitution specialization.
- Remove duplicate tests from UnifierTest.
@AntoniusW
Copy link
Collaborator Author

Comments have been addressed, please check if this is better now.

Copy link
Collaborator

@madmike200590 madmike200590 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the improvements, this looks really good now!

@madmike200590 madmike200590 merged commit 5360c49 into master Oct 7, 2020
@madmike200590 madmike200590 deleted the substitution_lazy_cloning branch October 7, 2020 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants