All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.chocosolver.solver.constraints.nary.cnf.LogOp Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of choco-solver, http://choco-solver.org/
 *
 * Copyright (c) 2024, IMT Atlantique. All rights reserved.
 *
 * Licensed under the BSD 4-clause license.
 *
 * See LICENSE file in the project root for full license information.
 */
package org.chocosolver.solver.constraints.nary.cnf;

import org.chocosolver.solver.variables.BoolVar;
import org.chocosolver.util.tools.ArrayUtils;

/**
 * Logical Operator, to ease clause definition.
 * 
* * @author Charles Prud'homme * @since 23 nov. 2010 */ public final class LogOp implements ILogical { public enum Operator { OR, AND; public static Operator flip(Operator operator) { if (Operator.OR.equals(operator)) { return Operator.AND; } else { return Operator.OR; } } } public enum Type { POSITIVE, NEGATIVE; public static Type flip(Type type) { if (Type.POSITIVE.equals(type)) { return Type.NEGATIVE; } else { return Type.POSITIVE; } } } Type type; Operator operator; private ILogical[] children; private BoolVar[] varsAsArray; LogOp(Operator operator, Type type, ILogical... children) { this.type = type; this.operator = operator; if (children == null) { this.children = new ILogical[0]; } else { this.children = children; } } /** * Create a conjunction, results in true if all of its operands are true * * @param op operands * @return a new logical operator */ public static LogOp and(ILogical... op) { return new LogOp(Operator.AND, Type.POSITIVE, op); } /** * Create a biconditional, results in true if and only if both operands are false * or both operands are true * * @param a operand * @param b operand * @return a new logical operator */ public static LogOp ifOnlyIf(ILogical a, ILogical b) { return and(implies(a, b), implies(b, a)); } /** * Create an implication, results in true if a is true` and b is true or a is false and c is true. * * @param a operand * @param b operand * @param c operand * @return a new logical operator */ public static LogOp ifThenElse(ILogical a, ILogical b, ILogical c) { try { ILogical na = negate(a); return or(and(a, b), and(na, c)); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } /** * Create an implication, results in true if a is false or b is true. * * @param a operand * @param b operand * @return a new logical operator */ public static LogOp implies(ILogical a, ILogical b) { try { ILogical na = negate(a); return or(na, b); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } /** * create a logical connection between ``b`` and ``tree``. * @param b operand * @param tree operand * @return a logical operator */ public static LogOp reified(BoolVar b, ILogical tree) { try { BoolVar nb = b.not(); ILogical ntree = negate(tree); return or(and(b, tree), and(nb, ntree)); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } /** * Create a disjunction, results in true whenever one or more of its operands are true * * @param op operands * @return a new logical operator */ public static LogOp or(ILogical... op) { return new LogOp(Operator.OR, Type.POSITIVE, op); } /** * Create an alternative denial, results in if at least one of its operands is false. * * @param op operands * @return a new logical operator */ public static LogOp nand(ILogical... op) { return new LogOp(Operator.AND, Type.NEGATIVE, op); } /** * Create a joint denial, results in `true` if all of its operands are false. * * @param op operands * @return a new logical operator */ public static LogOp nor(ILogical... op) { return new LogOp(Operator.OR, Type.NEGATIVE, op); } /** * Create an exclusive disjunction, results in true whenever both operands differ. * * @param a operand * @param b operand * @return a new logical operator */ public static LogOp xor(ILogical a, ILogical b) { try { ILogical na = negate(a); ILogical nb = negate(b); return or(and(a, nb), and(b, na)); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } /** * Create the logical complement of l. * * @param l operand * @return a new ILogical * @throws CloneNotSupportedException */ private static ILogical negate(ILogical l) throws CloneNotSupportedException { if (l.isLit()) { return ((BoolVar) l).not(); } else { LogOp n = (LogOp) l; LogOp na = n.clone(); na.type = Type.flip(and().type); return na; } } /** * Current tree is rooted with the logical operator op * * @param op operator checked * @return true if this is op */ public boolean is(Operator op) { return op.equals(operator); } /** * Current tree is rooted with NOT logical operator * * @return true if this is NOT */ public boolean isNot() { return type.equals(Type.NEGATIVE); } @Override public boolean isLit() { return false; } @Override public void setNot(boolean isNot) { throw new UnsupportedOperationException(); } /** * Returns the number of direct children of this * * @return number of children */ int getNbChildren() { return children.length; } /** * Check if at least one children is an OR logic tree * * @return true if this contains one OR logic tree */ boolean hasOrChild() { for (int i = 0; i < children.length; i++) { if (!children[i].isLit() && ((LogOp) children[i]).is(Operator.OR)) { return true; } } return false; } /** * Checks if at least one children is an AND logic tree * * @return true if this contains one AND logic tree */ boolean hasAndChild() { for (int i = 0; i < children.length; i++) { if (!children[i].isLit() && ((LogOp) children[i]).is(Operator.AND)) { return true; } } return false; } /** * Adds child to the current list of children of this * * @param child the logic tree to add */ public void addChild(ILogical child) { ILogical[] tmp = children; children = new ILogical[tmp.length + 1]; System.arraycopy(tmp, 0, children, 0, tmp.length); children[tmp.length] = child; varsAsArray = null; // force recomputation of varsArray } /** * Removes child from the current list of children of this * * @param child the logic tree to remove */ public void removeChild(ILogical child) { int i = 0; while (i < children.length && children[i] != child) { i++; } if (i == children.length) return; ILogical[] tmp = children; children = new ILogical[tmp.length - 1]; System.arraycopy(tmp, 0, children, 0, i); System.arraycopy(tmp, i + 1, children, i, tmp.length - i - 1); varsAsArray = null; // force recomputation of varsArray } /** * Returns the array of children of this. * null is a valid return value. * * @return an array of logic trees, null otherwise */ public ILogical[] getChildren() { return children; } /** * Returns the first AND logic tree within the list of children. * null is a valid return value. * * @return a AND logic tree if exists, null otherwise */ public ILogical getAndChild() { for (int i = 0; i < children.length; i++) { if (!children[i].isLit() && ((LogOp) children[i]).is(Operator.AND)) { return children[i]; } } return null; } /** * Returns the first child within the list of children, different from child. * null is a valid return value. * * @param child node to avoid * @return the first logic tree different from child if exists, null otherwise */ public ILogical getChildBut(ILogical child) { for (int i = 0; i < children.length; i++) { if (children[i] != child) { return children[i]; } } return null; } /** * Flip the boolean evaluation of this (recursive). */ public void flip() { type = Type.flip(type); operator = Operator.flip(operator); for (int i = 0; i < children.length; i++) { if (children[i].isLit()) { children[i] = ((BoolVar) children[i]).not(); } else { ((LogOp) children[i]).deny(); } } } /** * Flip the boolean operator of this (recursive). */ public void deny() { operator = Operator.flip(operator); for (int i = 0; i < children.length; i++) { if (children[i].isLit()) { children[i] = ((BoolVar) children[i]).not(); } else { ((LogOp) children[i]).deny(); } } } @Override public String toString() { StringBuilder st = new StringBuilder(); st.append('('); // st.append(Type.POSITIVE.equals(type) ? "(" : "not("); String op = (Type.POSITIVE.equals(type) ? "" : "n") + (Operator.AND.equals(operator) ? "and " : "or "); for (int i = 0; i < children.length; i++) { ILogical child = children[i]; if (child.isLit()) { st.append(((BoolVar) child).getName()); } else { st.append(child); } st.append(" ").append(op); } st.replace(st.length() - (op.length() + 1), st.length(), ""); st.append(')'); return st.toString(); } @Override public LogOp clone() throws CloneNotSupportedException { LogOp logOp = (LogOp) super.clone(); logOp.type = this.type; logOp.operator = this.operator; logOp.children = new ILogical[this.children.length]; for (int c = 0; c < children.length; c++) { if (children[c].isLit()) { logOp.children[c] = children[c]; } else { logOp.children[c] = ((LogOp) children[c]).clone(); } } return logOp; } /** * Extracts and returns the flatten array of BoolVar contained in this. * WARNING : a variable may appear more than once, redundancy is not checked! * * @return array of bool variables */ public BoolVar[] flattenBoolVar() { // if (varsAsArray == null) { buildVarsArray(); // } return varsAsArray; } public void cleanFlattenBoolVar() { for (int i = 0; i < children.length; i++) { if (!children[i].isLit()) { ((LogOp) children[i]).cleanFlattenBoolVar(); } } varsAsArray = null; } private void buildVarsArray() { final BoolVar[][] childrenVars = new BoolVar[children.length][]; for (int i = 0; i < children.length; i++) { if (children[i].isLit()) { childrenVars[i] = new BoolVar[]{(BoolVar) children[i]}; } else { childrenVars[i] = ((LogOp) children[i]).flattenBoolVar(); } } varsAsArray = ArrayUtils.flatten(childrenVars); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy