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

Count globals rebased #245

Merged
merged 7 commits into from
Mar 6, 2023
Merged

Count globals rebased #245

merged 7 commits into from
Mar 6, 2023

Conversation

JoD
Copy link
Collaborator

@JoD JoD commented Mar 2, 2023

No description provided.

@@ -582,3 +585,65 @@ def deepcopy(self, memodict={}):
return Cumulative(*copied_args)


class GlobalCardinalityCount(GlobalConstraint):
"""
GlobalCardinalityCount(a,gcc): Collect the number of occurrences of each value 0..a.ub in gcc.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Can a contain arbitrary expressions, not only pure values? I guess yes, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I do not yet see why we have this upper bound invariant (len(gcc) == ub + 1). Whether gcc is large or small compared to a does not really matter, len(gcc) just tells us something about the number of simple cardinality constraints implied by this global one. E.g., a=[1,2,5,4], gcc=[0,1,1] would simply be equivalent to Count(a,0)=0 & Count(a,1)=1 & Count(a,2)=1. And a=[1,2,5,4], gcc=[0,1,1,0,1,1] would simply be Count(a,0)=0 & Count(a,1)=1 & Count(a,2)=1 & Count(a,3)=0 & Count(a,4)=1 & Count(a,5)=1. What would be the problem here?

Copy link
Collaborator

Choose a reason for hiding this comment

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

with the bounds calculations pr that would be possible, as is only values or intvars are allowed

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

with the bounds calculations pr that would be possible, as is only values or intvars are allowed

Can you elaborate what you mean?

@JoD
Copy link
Collaborator Author

JoD commented Mar 2, 2023

@Wout4 Review finished, let me know what you think!

@Wout4
Copy link
Collaborator

Wout4 commented Mar 2, 2023

I added a change to the bounds_calucation pr to calculate a.ub appropriatly for gcc
Also fixed a bug equivalent to the one in table decomposition

Copy link
Collaborator

@tias tias left a comment

Choose a reason for hiding this comment

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

looks good, left 2 readability comments but can also be merged as is.

def value(self):
a, gcc = self.args
gval = [argval(y) for y in gcc]
return all([gval[i] == Count(a,i).value() for i in range(len(gcc))])
Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess you could do all(self.decompose()).value()? Would be cleaner? or not possible?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes that works, changed it!

Count(arr,val) can only be decomposed if it's part of a comparison
"""
arr, val = self.args
return [eval_comparison(cmp_op, Operator('sum',[ai==val for ai in arr]), cmp_rhs)]
Copy link
Collaborator

Choose a reason for hiding this comment

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

bit clumsy formulation.
why not using 'sum' as in value?

smth like cnt = sum(ai == val for ai in arr); eval_comparison(cmp_op, cnt, cmp_rhs)?
could be more readable...

@Wout4 Wout4 merged commit 7c162dd into master Mar 6, 2023
Copy link
Collaborator Author

@JoD JoD left a comment

Choose a reason for hiding this comment

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

test

@@ -582,3 +585,65 @@ def deepcopy(self, memodict={}):
return Cumulative(*copied_args)


class GlobalCardinalityCount(GlobalConstraint):
"""
GlobalCardinalityCount(a,gcc): Collect the number of occurrences of each value 0..a.ub in gcc.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I do not yet see why we have this upper bound invariant (len(gcc) == ub + 1). Whether gcc is large or small compared to a does not really matter, len(gcc) just tells us something about the number of simple cardinality constraints implied by this global one. E.g., a=[1,2,5,4], gcc=[0,1,1] would simply be equivalent to Count(a,0)=0 & Count(a,1)=1 & Count(a,2)=1. And a=[1,2,5,4], gcc=[0,1,1,0,1,1] would simply be Count(a,0)=0 & Count(a,1)=1 & Count(a,2)=1 & Count(a,3)=0 & Count(a,4)=1 & Count(a,5)=1. What would be the problem here?

def value(self):
a, gcc = self.args
gval = [argval(y) for y in gcc]
return all([gval[i] == Count(a,i).value() for i in range(len(gcc))])
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Simpler, based on line 601: return all([Count(a, i).value() == argval(v) for i, v in enumerate(gcc)])


def deepcopy(self, memodict={}):
"""
Return a deep copy of the AllDifferentExceptO global constraint
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

GlobalCardinalityCount instead of AllDifferentExcept0

bv = cp.boolvar(5)
self.assertTrue(cp.Model(cp.Xor(bv)).solve())
self.assertTrue(cp.Xor(bv).value())

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Do these tests belong here? I'm happy to let them piggy-back, but maybe something went wrong when splitting features / rebasing the branch.

@@ -582,3 +585,65 @@ def deepcopy(self, memodict={}):
return Cumulative(*copied_args)


class GlobalCardinalityCount(GlobalConstraint):
"""
GlobalCardinalityCount(a,gcc): Collect the number of occurrences of each value 0..a.ub in gcc.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

with the bounds calculations pr that would be possible, as is only values or intvars are allowed

Can you elaborate what you mean?

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