Skip to content

Commit

Permalink
interface for exactly-k constraints and totalizer encoding for exactly k
Browse files Browse the repository at this point in the history
  • Loading branch information
czengler committed May 30, 2016
1 parent 74cbbbf commit 7169693
Show file tree
Hide file tree
Showing 6 changed files with 349 additions and 30 deletions.
16 changes: 16 additions & 0 deletions src/main/java/org/logicng/cardinalityconstraints/CCConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,14 @@ public enum AMK_ENCODER {TOTALIZER, MODULAR_TOTALIZER, CARDINALITY_NETWORK, BEST

public enum ALK_ENCODER {TOTALIZER, BEST}

public enum EXK_ENCODER {TOTALIZER, BEST}

public enum BIMANDER_GROUP_SIZE {HALF, SQRT, FIXED}

final AMO_ENCODER amoEncoder;
final AMK_ENCODER amkEncoder;
final ALK_ENCODER alkEncoder;
final EXK_ENCODER exkEncoder;
final BIMANDER_GROUP_SIZE bimanderGroupSize;
final int bimanderFixedGroupSize;
final int nestingGroupSize;
Expand All @@ -62,6 +65,7 @@ public static class Builder {
private AMO_ENCODER amoEncoder = AMO_ENCODER.BEST;
private AMK_ENCODER amkEncoder = AMK_ENCODER.BEST;
private ALK_ENCODER alkEncoder = ALK_ENCODER.BEST;
private EXK_ENCODER exkEncoder = EXK_ENCODER.BEST;
private BIMANDER_GROUP_SIZE bimanderGroupSize = BIMANDER_GROUP_SIZE.SQRT;
private int bimanderFixedGroupSize = 3;
private int nestingGroupSize = 4;
Expand Down Expand Up @@ -98,6 +102,16 @@ public Builder alkEncoding(final ALK_ENCODER alkEncoder) {
return this;
}

/**
* Sets the encoder for exactly-k constraints. The default value is {@code BEST}.
* @param exkEncoder the exactly-k encoder
* @return the builder
*/
public Builder exkEncoding(final EXK_ENCODER exkEncoder) {
this.exkEncoder = exkEncoder;
return this;
}

/**
* Sets the group size for the bimander encoding. The default value is {@code SQRT}.
* @param bimanderGroupSize the bimander encoding group size
Expand Down Expand Up @@ -167,6 +181,7 @@ private CCConfig(final Builder builder) {
this.amoEncoder = builder.amoEncoder;
this.amkEncoder = builder.amkEncoder;
this.alkEncoder = builder.alkEncoder;
this.exkEncoder = builder.exkEncoder;
this.bimanderGroupSize = builder.bimanderGroupSize;
this.bimanderFixedGroupSize = builder.bimanderFixedGroupSize;
this.nestingGroupSize = builder.nestingGroupSize;
Expand All @@ -180,6 +195,7 @@ public String toString() {
sb.append("amoEncoder=").append(this.amoEncoder).append("\n");
sb.append("amkEncoder=").append(this.amkEncoder).append("\n");
sb.append("alkEncoder=").append(this.alkEncoder).append("\n");
sb.append("exkEncoder=").append(this.exkEncoder).append("\n");
sb.append("bimanderGroupSize=").append(this.bimanderGroupSize).append("\n");
sb.append("bimanderFixedGroupSize=").append(this.bimanderFixedGroupSize).append("\n");
sb.append("nestingGroupSize=").append(this.nestingGroupSize).append("\n");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
///////////////////////////////////////////////////////////////////////////
// __ _ _ ________ //
// / / ____ ____ _(_)____/ | / / ____/ //
// / / / __ \/ __ `/ / ___/ |/ / / __ //
// / /___/ /_/ / /_/ / / /__/ /| / /_/ / //
// /_____/\____/\__, /_/\___/_/ |_/\____/ //
// /____/ //
// //
// The Next Generation Logic Library //
// //
///////////////////////////////////////////////////////////////////////////
// //
// Copyright 2015-2016 Christoph Zengler //
// //
// Licensed under the Apache License, Version 2.0 (the "License"); //
// you may not use this file except in compliance with the License. //
// You may obtain a copy of the License at //
// //
// http://www.apache.org/licenses/LICENSE-2.0 //
// //
// Unless required by applicable law or agreed to in writing, software //
// distributed under the License is distributed on an "AS IS" BASIS, //
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or //
// implied. See the License for the specific language governing //
// permissions and limitations under the License. //
// //
///////////////////////////////////////////////////////////////////////////

/*****************************************************************************************
* Open-WBO -- Copyright (c) 2013-2015, Ruben Martins, Vasco Manquinho, Ines Lynce
* <p>
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
* <p>
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* <p>
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*****************************************************************************************/

package org.logicng.cardinalityconstraints;

import org.logicng.formulas.Formula;
import org.logicng.formulas.FormulaFactory;
import org.logicng.formulas.Variable;

import java.util.List;

/**
* Encodes that exactly 'rhs' variables can be assigned value true. Uses the totalizer encoding for
* translating the cardinality constraint into CNF.
* @version 1.1
* @since 1.1
*/
final class CCEXKTotalizer implements CCExactlyK {

private final CCTotalizer totalizer;

/**
* Constructs a new totalizer.
* @param f the formula factory
*/
CCEXKTotalizer(final FormulaFactory f) {
this.totalizer = new CCTotalizer(f);
}


@Override
public List<Formula> build(final Variable[] vars, int rhs) {
return this.totalizer.buildEXK(vars, rhs);
}

@Override
public CCIncrementalData incrementalData() {
return this.totalizer.incrementalData();
}

@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
56 changes: 44 additions & 12 deletions src/main/java/org/logicng/cardinalityconstraints/CCEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import org.logicng.collections.ImmutableFormulaList;
import org.logicng.configurations.Configuration;
import org.logicng.configurations.ConfigurationType;
import org.logicng.formulas.CType;
import org.logicng.formulas.FType;
import org.logicng.formulas.Formula;
import org.logicng.formulas.FormulaFactory;
Expand All @@ -42,7 +41,6 @@

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/**
Expand Down Expand Up @@ -78,6 +76,7 @@ public class CCEncoder {
private CCAMKModularTotalizer amkModularTotalizer;
private CCAMKTotalizer amkTotalizer;
private CCALKTotalizer alkTotalizer;
private CCEXKTotalizer exkTotalizer;

/**
* Constructs a new cardinality constraint encoder with a given configuration.
Expand Down Expand Up @@ -173,16 +172,17 @@ private List<Formula> encodeConstraint(final PBConstraint cc) {
if (cc.rhs() == 1)
return this.exo(ops);
else {
final PBConstraint le = this.f.cc(CType.LE, cc.rhs(), ops);
final PBConstraint ge = this.f.cc(CType.GE, cc.rhs(), ops);
if (le.type() == FType.FALSE || ge.type() == FType.FALSE)
return Collections.singletonList((Formula) this.f.falsum());
final List<Formula> list = new LinkedList<>();
if (le.type() != FType.TRUE)
list.addAll(this.encode(le).toList());
if (ge.type() != FType.TRUE)
list.addAll(this.encode(ge).toList());
return list;
// final PBConstraint le = this.f.cc(CType.LE, cc.rhs(), ops);
// final PBConstraint ge = this.f.cc(CType.GE, cc.rhs(), ops);
// if (le.type() == FType.FALSE || ge.type() == FType.FALSE)
// return Collections.singletonList((Formula) this.f.falsum());
// final List<Formula> list = new LinkedList<>();
// if (le.type() != FType.TRUE)
// list.addAll(this.encode(le).toList());
// if (ge.type() != FType.TRUE)
// list.addAll(this.encode(ge).toList());
// return list;
return this.exk(ops, cc.rhs());
}
default:
throw new IllegalArgumentException("Unknown pseudo-Boolean comparator: " + cc.comparator());
Expand Down Expand Up @@ -373,6 +373,38 @@ private List<Formula> alk(final Variable[] vars, int rhs) {
}
}

/**
* Encodes an exactly-k constraint.
* @param vars the variables of the constraint
* @param rhs the right hand side of the constraint
* @return the CNF encoding of the constraint
*/
private List<Formula> exk(final Variable[] vars, int rhs) {
if (rhs < 0)
throw new IllegalArgumentException("Invalid right hand side of cardinality constraint: " + rhs);
if (rhs > vars.length)
return Collections.singletonList((Formula) this.f.falsum());
final List<Formula> result = new ArrayList<>();
if (rhs == 0) {
for (final Variable var : vars)
result.add(var.negate());
return result;
}
if (rhs == vars.length) {
Collections.addAll(result, vars);
return result;
}
switch (this.config().exkEncoder) {
case TOTALIZER:
case BEST:
if (this.exkTotalizer == null)
this.exkTotalizer = new CCEXKTotalizer(this.f);
return this.exkTotalizer.build(vars, rhs);
default:
throw new IllegalStateException("Unknown exactly-k encoder: " + this.config().exkEncoder);
}
}

/**
* Encodes an at-lest-k constraint for incremental usage.
* @param vars the variables of the constraint
Expand Down
56 changes: 56 additions & 0 deletions src/main/java/org/logicng/cardinalityconstraints/CCExactlyK.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
///////////////////////////////////////////////////////////////////////////
// __ _ _ ________ //
// / / ____ ____ _(_)____/ | / / ____/ //
// / / / __ \/ __ `/ / ___/ |/ / / __ //
// / /___/ /_/ / /_/ / / /__/ /| / /_/ / //
// /_____/\____/\__, /_/\___/_/ |_/\____/ //
// /____/ //
// //
// The Next Generation Logic Library //
// //
///////////////////////////////////////////////////////////////////////////
// //
// Copyright 2015-2016 Christoph Zengler //
// //
// Licensed under the Apache License, Version 2.0 (the "License"); //
// you may not use this file except in compliance with the License. //
// You may obtain a copy of the License at //
// //
// http://www.apache.org/licenses/LICENSE-2.0 //
// //
// Unless required by applicable law or agreed to in writing, software //
// distributed under the License is distributed on an "AS IS" BASIS, //
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or //
// implied. See the License for the specific language governing //
// permissions and limitations under the License. //
// //
///////////////////////////////////////////////////////////////////////////

package org.logicng.cardinalityconstraints;

import org.logicng.formulas.Formula;
import org.logicng.formulas.Variable;

import java.util.List;

/**
* The interface for exactly-k (ALK) cardinality constraints.
* @version 1.1
* @since 1.1
*/
interface CCExactlyK {
/**
* Builds a cardinality constraint of the form {@code var_1 + var_2 + ... + var_n = k}.
* @param vars the variables {@code var_1 ... var_n}
* @param rhs the right hand side {@code k} of the constraint
* @return the CNF encoding of the cardinality constraint
* @throws IllegalArgumentException if the right hand side of the cardinality constraint is negative
*/
List<Formula> build(final Variable[] vars, int rhs);

/**
* Returns the incremental data for the current encoded constraint.
* @return the incremental data for the current encoded constraint
*/
CCIncrementalData incrementalData();
}
58 changes: 40 additions & 18 deletions src/main/java/org/logicng/cardinalityconstraints/CCTotalizer.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
*/
final class CCTotalizer {

private enum Bound {LOWER, UPPER}
private enum Bound {LOWER, UPPER, BOTH}

private final FormulaFactory f;
private LNGVector<Variable> cardinalityInvars;
Expand All @@ -89,13 +89,7 @@ private enum Bound {LOWER, UPPER}
* @throws IllegalArgumentException if the right hand side of the constraint was negative
*/
List<Formula> buildAMK(final Variable[] vars, int rhs) {
this.result = new ArrayList<>();
this.cardinalityInvars = new LNGVector<>(vars.length);
final LNGVector<Variable> cardinalityOutvars = new LNGVector<>(vars.length);
for (final Variable var : vars) {
this.cardinalityInvars.push(var);
cardinalityOutvars.push(this.f.newCCVariable());
}
final LNGVector<Variable> cardinalityOutvars = this.initializeConstraint(vars);
this.incData = new CCIncrementalData(this.f, CCConfig.AMK_ENCODER.TOTALIZER, rhs, cardinalityOutvars);
this.toCNF(cardinalityOutvars, rhs, Bound.UPPER);
assert this.cardinalityInvars.size() == 0;
Expand All @@ -112,19 +106,47 @@ List<Formula> buildAMK(final Variable[] vars, int rhs) {
* @throws IllegalArgumentException if the right hand side of the constraint was negative
*/
List<Formula> buildALK(final Variable[] vars, int rhs) {
final LNGVector<Variable> cardinalityOutvars = this.initializeConstraint(vars);
this.incData = new CCIncrementalData(this.f, CCConfig.ALK_ENCODER.TOTALIZER, rhs, cardinalityOutvars);
this.toCNF(cardinalityOutvars, rhs, Bound.LOWER);
assert this.cardinalityInvars.size() == 0;
for (int i = 0; i < rhs; i++)
this.result.add(cardinalityOutvars.get(i));
return this.result;
}

/**
* Builds an exactly-k constraint.
* @param vars the variables
* @param rhs the right-hand side
* @return the constraint
* @throws IllegalArgumentException if the right hand side of the constraint was negative
*/
List<Formula> buildEXK(final Variable[] vars, int rhs) {
final LNGVector<Variable> cardinalityOutvars = this.initializeConstraint(vars);
this.toCNF(cardinalityOutvars, rhs, Bound.BOTH);
assert this.cardinalityInvars.size() == 0;
for (int i = 0; i < rhs; i++)
this.result.add(cardinalityOutvars.get(i));
for (int i = rhs; i < cardinalityOutvars.size(); i++)
this.result.add(cardinalityOutvars.get(i).negate());
return this.result;
}

/**
* Initializes the constraint.
* @param vars the variables
* @return the auxiliary variables
*/
private LNGVector<Variable> initializeConstraint(final Variable[] vars) {
this.result = new ArrayList<>();
this.cardinalityInvars = new LNGVector<>(vars.length);
final LNGVector<Variable> cardinalityOutvars = new LNGVector<>(vars.length);
for (final Variable var : vars) {
this.cardinalityInvars.push(var);
cardinalityOutvars.push(this.f.newCCVariable());
}
this.incData = new CCIncrementalData(this.f, CCConfig.ALK_ENCODER.TOTALIZER, rhs, cardinalityOutvars);
this.toCNF(cardinalityOutvars, rhs, Bound.LOWER);
assert this.cardinalityInvars.size() == 0;
for (int i = 0; i < rhs; i++)
this.result.add(cardinalityOutvars.get(i));
return this.result;
return cardinalityOutvars;
}

/**
Expand Down Expand Up @@ -157,10 +179,10 @@ private void toCNF(final LNGVector<Variable> vars, int rhs, final Bound bound) {
right.push(this.f.newCCVariable());
}
}
if (bound == Bound.UPPER)
if (bound == Bound.UPPER || bound == Bound.BOTH)
this.adderAMK(left, right, vars, rhs);
else
this.adderALK(left, right, vars, rhs);
if (bound == Bound.LOWER || bound == Bound.BOTH)
this.adderALK(left, right, vars);
if (left.size() > 1)
this.toCNF(left, rhs, bound);
if (right.size() > 1)
Expand All @@ -187,7 +209,7 @@ else if (j == 0)
}

private void adderALK(final LNGVector<Variable> left, final LNGVector<Variable> right,
final LNGVector<Variable> output, int rhs) {
final LNGVector<Variable> output) {
assert output.size() == left.size() + right.size();
for (int i = 0; i <= left.size(); i++) {
for (int j = 0; j <= right.size(); j++) {
Expand Down
Loading

0 comments on commit 7169693

Please sign in to comment.