Skip to content

[WIP] [TypeChecker] Score disjunctions and constraint components to prune search space #9220

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

Closed
wants to merge 2 commits into from

Conversation

xedin
Copy link
Contributor

@xedin xedin commented May 3, 2017

Before trying to solve constraint system components:

  1. Score constraint components/buckets based on how many disjunctions
    they have, that helps to prune some of the branches with incorrect
    solutions early which limits overall depth of the search.

  2. In each of the constraint component score disjunctions
    based on how much information do we have about them, the
    more information we have the easier it is to prune search
    space of incorrect branches earlier.

  3. When dealing with explicit coercions apply their conversions
    early to avoid searching for solutions with incorrect types.

@xedin xedin requested a review from rudkx May 3, 2017 07:58
@xedin
Copy link
Contributor Author

xedin commented May 3, 2017

@swift-ci please test

@xedin
Copy link
Contributor Author

xedin commented May 3, 2017

@swift-ci Please test source compatibility

1 similar comment
@xedin
Copy link
Contributor Author

xedin commented May 3, 2017

@swift-ci Please test source compatibility

Copy link
Member

@DougGregor DougGregor left a comment

Choose a reason for hiding this comment

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

Interesting approach!

typeVar, constraints, ConstraintGraph::GatheringKind::EquivalenceClass);

for (auto constraint : constraints) {
if (constraint->getKind() != ConstraintKind::Conversion)
Copy link
Member

Choose a reason for hiding this comment

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

There are other constraints like SubType constraints that have similar properties. I suggest switching on constraint->getKind() so it's clear that we've thought about all of these.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, will do!

// that helps to avoid invalid disjunction choices.
if (LHS->isEqual(typeVar) && !RHS->hasUnresolvedType()) {
// No type variables, means pretty good chance that
// this is conversion to some concerete type, which
Copy link
Member

Choose a reason for hiding this comment

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

Typo "concerete".

@xedin xedin force-pushed the disjunction-scoring branch from d61b753 to 52bede3 Compare June 18, 2017 00:21
@xedin
Copy link
Contributor Author

xedin commented Jun 18, 2017

@DougGregor @rudkx I've rebased everything with the latest master and made conversion propagation a part of DisjunctionChoice solving, and addressed both of the comments.

@xedin
Copy link
Contributor Author

xedin commented Jun 18, 2017

@swift-ci please test

@xedin xedin force-pushed the disjunction-scoring branch from 52bede3 to c0db10b Compare June 18, 2017 00:50
@swiftlang swiftlang deleted a comment from swift-ci Jun 18, 2017
@xedin
Copy link
Contributor Author

xedin commented Jun 18, 2017

Looks like one of the expressions is no longer too complex :)

@xedin
Copy link
Contributor Author

xedin commented Jun 18, 2017

@swift-ci please test

@swiftlang swiftlang deleted a comment from swift-ci Jun 18, 2017
@swiftlang swiftlang deleted a comment from swift-ci Jun 18, 2017
@xedin
Copy link
Contributor Author

xedin commented Jun 18, 2017

@swift-ci please test source compatibility

for (unsigned component = 0; component != numComponents; ++component)
componentOrdering.push_back(component);
// Now sort the components in the ordering based on the scores of the buckets.
std::sort(
Copy link
Member

Choose a reason for hiding this comment

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

You could sort the buckets directly, so you don't have to separately maintain the componentOrdering. std::sort will std::move the ConstraintBucket instances, so it shouldn't be any more expensive.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've separated ordered set from the original one because components is a vector and some of the code down the line relies on the original indexes the way they are produced by constraint graph e.g. associating constraints into their buckets and orphan handling logic. I think that should be fixed down the road but I just didn't want to tackle it right away...

};

auto evaluateDisjunction = [&](Constraint *contender, float score) {
// Score has to be strictly greather than best because that would
Copy link
Member

Choose a reason for hiding this comment

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

typo "greather"

auto evaluateDisjunction = [&](Constraint *contender, float score) {
// Score has to be strictly greather than best because that would
// allow us to apply disjunctions in their semantic order, which gives
// better changes of avoiding of the the obviously wrong choices with
Copy link
Member

Choose a reason for hiding this comment

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

"the the"

// everything else being equal.
if (score > bestScore ||
(score == bestScore &&
getDisjunctionId(bestDisjunction) > getDisjunctionId(contender))) {
Copy link
Member

Choose a reason for hiding this comment

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

Rather than doing a bit of work to dig out type variable IDs to compare (which isn't always going to give an ordering), why not keep track of the index of the best option within the disjunction? It'll be more predictable and eliminate the need for getDisjunctionId.

@@ -1900,6 +1901,10 @@ class ConstraintSystem {
/// reads the type from the ConstraintSystem expression type map.
Type getResultType(const AbstractClosureExpr *E);

/// Determine if the given constraint represents explicit conversion,
/// e.g. coercion constraint "as X" which forms a disjunction.
bool isExplicitConversionConstraint(Constraint *constraint);
Copy link
Member

Choose a reason for hiding this comment

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

I'd love to have "disjunction" in the name, too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good, thanks!

@@ -2728,6 +2748,48 @@ class DisjunctionChoice {
return decl->isOperator() ? decl : nullptr;
}
};

class ConstraintBucket {
Copy link
Member

Choose a reason for hiding this comment

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

I'm very glad to have this functionality split out into a separate class, but please document what this class does via comments.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do!

@xedin
Copy link
Contributor Author

xedin commented Jun 24, 2017

@swift-ci please test source compatibility

@xedin xedin force-pushed the disjunction-scoring branch from c0db10b to 56327c9 Compare June 30, 2017 08:19
@xedin
Copy link
Contributor Author

xedin commented Jun 30, 2017

@swift-ci please test

@swiftlang swiftlang deleted a comment from swift-ci Jun 30, 2017
@swiftlang swiftlang deleted a comment from swift-ci Jun 30, 2017
@xedin
Copy link
Contributor Author

xedin commented Jun 30, 2017

@swift-ci please test source compatibility

@xedin xedin force-pushed the disjunction-scoring branch from 56327c9 to a290307 Compare July 14, 2017 22:24
@xedin
Copy link
Contributor Author

xedin commented Jul 14, 2017

@swift-ci please smoke test

@xedin xedin force-pushed the disjunction-scoring branch from a290307 to 80edd2b Compare July 17, 2017 22:51
@xedin
Copy link
Contributor Author

xedin commented Jul 17, 2017

@swift-ci please smoke test OS X platform

@xedin xedin force-pushed the disjunction-scoring branch from 80edd2b to 804aa60 Compare August 3, 2017 00:01
@xedin
Copy link
Contributor Author

xedin commented Aug 3, 2017

@swift-ci please smoke test OS X platform

@xedin xedin force-pushed the disjunction-scoring branch from 804aa60 to 906820a Compare August 18, 2017 18:41
@xedin
Copy link
Contributor Author

xedin commented Aug 18, 2017

@swift-ci please clean smoke test OS X platform

@xedin
Copy link
Contributor Author

xedin commented Aug 18, 2017

@swift-ci please smoke test OS X platform

@swiftlang swiftlang deleted a comment from swift-ci Aug 18, 2017
@xedin xedin force-pushed the disjunction-scoring branch from 5e41cb7 to 14458b2 Compare August 18, 2017 22:52
@xedin
Copy link
Contributor Author

xedin commented Aug 18, 2017

@swift-ci please smoke test OS X platform

@xedin xedin force-pushed the disjunction-scoring branch from 14458b2 to d0bbed4 Compare August 22, 2017 19:43
@xedin
Copy link
Contributor Author

xedin commented Aug 22, 2017

@swift-ci please smoke test OS X platform

@xedin xedin force-pushed the disjunction-scoring branch from d0bbed4 to ec31877 Compare August 24, 2017 02:05
@xedin
Copy link
Contributor Author

xedin commented Aug 24, 2017

@swift-ci please smoke test

xedin added 2 commits August 24, 2017 13:28
…une search space

Before trying to solve constraint system components:

1. Score constraint components/buckets based on how many disjunctions
   they have, that helps to prune some of the branches with incorrect
   solutions early which limits overall depth of the search.

2. In each of the constraint components, score disjunctions
   based on how much information do we have about them, the
   more information we have the easier it is to prune search
   space of incorrect branches earlier.

3. When dealing with explicit coercions apply their conversions
   early to avoid searching for solutions with incorrect types.
… selection logic

Move disjunction selection logic one level up from the `solveSimplified`
which allows to simplify its logic and avoid collecting disjunctions multiple times
for each solver step.
@xedin xedin force-pushed the disjunction-scoring branch from ec31877 to 42b17ab Compare August 24, 2017 21:10
@xedin
Copy link
Contributor Author

xedin commented Aug 24, 2017

@swift-ci please smoke test OS X platform

@felix91gr
Copy link
Contributor

I can’t look at the code, but I’d like to ask about the algorithm per se.

I believe you’re solving a CSP here, right? By doing some variant of backtracking. Picking the variables with the least amount of options, so that you can branch less early and reduce the search space quickly. Am I right, there?

Um... if I am, I was thinking that maybe you could use a Heap (heapify your array/vector of variables). That way, the ordering step (O(nlogn)) is ommited, and then the whole solving process might become less costly.

I don’t know if it’s the best option, but in many heuristic algorithms that’s a classic :). In this case, CSP isn’t a “search” like the problems heuristics usually are used for... but the “score” you’re using fits the classic definition of “heuristic”. And therefore maybe it calls for similar tools 🤷🏻‍♂️🙂

I’d like to know the details of this problem. It looks really interesting :)

@xedin
Copy link
Contributor Author

xedin commented Sep 4, 2017

@felix91gr It's close but not really, the algorithm/heuristic is actually trying to figure out which disjunction (with N choices) to use first to possibly reduce the depth of the search, the problem we have is that type variable domains are not known until all disjunction choices are all picked, only after that we can go ahead and pick bindings for type variables and check their satisfiability to constraints we have, which makes it more complicated comparing to classing CSP where most of the algorithms are based on the fixed variable domains.

@felix91gr
Copy link
Contributor

Ahh. I see. Then, it’s like a search for trying “easy disjunctions” first. That search happens inside one step of assigning variables and checking constraint satisfiability. And I guess you try with every disjunction in the list?

(Do we have a documentation on this CS algorithm? I’d like to learn about it and work on it)

@xedin
Copy link
Contributor Author

xedin commented Sep 5, 2017

@felix91gr Kind of yes, but it doesn't actually assign anything, it's impossible really until all disjunction choices are set, but if something else did assign type to one of the type variables related to the disjunction it adds to it's score because it's more likely to filter out some of the branches.

Unfortunately we don't really have much documentation when it comes to expression type checker but we are working on it...

@felix91gr
Copy link
Contributor

We could talk on twitter (or anything). I could write an outline of the problem and algorithm after I understand it. It’ll be a pleasure.

@felix91gr
Copy link
Contributor

(And maybe add some drawings)

@xedin
Copy link
Contributor Author

xedin commented Sep 5, 2017

@felix91gr Thanks, I'll figure out if it works at all or not and let you know! I've actually made it a separate PR for it #11723

@felix91gr
Copy link
Contributor

Bah. Pardon me. I’m very tired...
You already have something. How can I help? Does it need proofreading, visualization, completion...?

I want this algorithm to be written somewhere, that way people (like me) who aren’t that llvm/cpp savvy, can help from the algorithmic side of it. It would also help polishing the implementation, if need be.

My point is, you have my axe hands.

@felix91gr
Copy link
Contributor

Oh, I see. Cool! Best of luck... and if you need me, just @ me somewhere ;)

@xedin
Copy link
Contributor Author

xedin commented Sep 5, 2017

Will do, thanks!

@xedin xedin closed this Sep 19, 2017
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

Successfully merging this pull request may close these issues.

3 participants