
org.xcsp.common.predicates.XNodeParent Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xcsp3-tools Show documentation
Show all versions of xcsp3-tools Show documentation
Java Tools for parsing XCSP3 instances, compiling JvCSP3 models, and checking solutions. For more information about XCSP3, follow www.xcsp.org
The newest version!
/*
* Copyright (c) 2016 XCSP3 Team ([email protected])
*
* 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:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* 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.xcsp.common.predicates;
import static java.util.stream.Collectors.toList;
import static org.xcsp.common.Types.TypeExpr.ABS;
import static org.xcsp.common.Types.TypeExpr.ADD;
import static org.xcsp.common.Types.TypeExpr.AND;
import static org.xcsp.common.Types.TypeExpr.DIST;
import static org.xcsp.common.Types.TypeExpr.EQ;
import static org.xcsp.common.Types.TypeExpr.LE;
import static org.xcsp.common.Types.TypeExpr.LONG;
import static org.xcsp.common.Types.TypeExpr.LT;
import static org.xcsp.common.Types.TypeExpr.MAX;
import static org.xcsp.common.Types.TypeExpr.MIN;
import static org.xcsp.common.Types.TypeExpr.MUL;
import static org.xcsp.common.Types.TypeExpr.NEG;
import static org.xcsp.common.Types.TypeExpr.NOT;
import static org.xcsp.common.Types.TypeExpr.OR;
import static org.xcsp.common.Types.TypeExpr.SUB;
import static org.xcsp.common.Types.TypeExpr.IMP;
import static org.xcsp.common.predicates.MatcherInterface.any;
import static org.xcsp.common.predicates.MatcherInterface.any_add_val;
import static org.xcsp.common.predicates.MatcherInterface.anyc;
import static org.xcsp.common.predicates.MatcherInterface.not;
import static org.xcsp.common.predicates.MatcherInterface.sub;
import static org.xcsp.common.predicates.MatcherInterface.val;
import static org.xcsp.common.predicates.MatcherInterface.var;
import static org.xcsp.common.predicates.MatcherInterface.var_add_val;
import static org.xcsp.common.predicates.MatcherInterface.AbstractOperation.relop;
import static org.xcsp.common.predicates.MatcherInterface.AbstractOperation.symop;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.xcsp.common.IVar;
import org.xcsp.common.Range;
import org.xcsp.common.Types.TypeExpr;
import org.xcsp.common.Utilities;
import org.xcsp.common.predicates.MatcherInterface.Matcher;
import org.xcsp.parser.entries.XConstraints.XParameter;
/**
* The class used for representing a parent node in a syntactic tree.
*
* @author Christophe Lecoutre
*/
public class XNodeParent extends XNode {
public static XNodeParent build(TypeExpr type, Object... os) {
os = Stream.of(os).flatMap(o -> o instanceof Stream ? (Stream>) o : Stream.of(o)).toArray();
Utilities.control(type.arityMin <= os.length && os.length <= type.arityMax, "The arity (number of sons) is not valid");
List> sons = Stream.of(os).map(o -> {
if (o instanceof XNode)
return (XNode) o;
if (o instanceof IVar)
return new XNodeLeaf(TypeExpr.VAR, o);
if (o instanceof Byte || o instanceof Short || o instanceof Integer || o instanceof Long)
return new XNodeLeaf(TypeExpr.LONG, ((Number) o).longValue());
if (o instanceof String)
return new XNodeLeaf(TypeExpr.SYMBOL, o);
if (o instanceof XParameter)
return new XNodeLeaf(TypeExpr.PAR, ((XParameter) o).number);
return (XNode) Utilities.control(false,
"Bad form: have you used equal, different , lessThan,... instead of eq, ne, lt,... ?\n" + "The class causing problem is " + o.getClass());
}).collect(Collectors.toList()); // toArray(XNode[]::new);
return new XNodeParent(type, sons);
}
public static XNodeParent abs(Object operand) {
return build(TypeExpr.ABS, operand);
}
public static XNodeParent neg(Object operand) {
return build(TypeExpr.NEG, operand);
}
public static XNodeParent sqr(Object operand) {
return build(TypeExpr.SQR, operand);
}
public static XNodeParent add(Object... operands) {
return build(TypeExpr.ADD, operands);
}
public static XNodeParent sub(Object operand1, Object operand2) {
return build(TypeExpr.SUB, operand1, operand2);
}
public static XNodeParent mul(Object... operands) {
return build(TypeExpr.MUL, operands);
}
public static XNodeParent div(Object operand1, Object operand2) {
return build(TypeExpr.DIV, operand1, operand2);
}
public static XNodeParent mod(Object operand1, Object operand2) {
return build(TypeExpr.MOD, operand1, operand2);
}
public static XNodeParent pow(Object operand1, Object operand2) {
return build(TypeExpr.POW, operand1, operand2);
}
public static XNodeParent min(Object... operands) {
return build(TypeExpr.MIN, operands);
}
public static XNodeParent max(Object... operands) {
return build(TypeExpr.MAX, operands);
}
public static XNodeParent dist(Object operand1, Object operand2) {
return build(TypeExpr.DIST, operand1, operand2);
}
public static XNodeParent lt(Object operand1, Object operand2) {
return build(TypeExpr.LT, operand1, operand2);
}
public static XNodeParent le(Object operand1, Object operand2) {
return build(TypeExpr.LE, operand1, operand2);
}
public static XNodeParent ge(Object operand1, Object operand2) {
return build(TypeExpr.GE, operand1, operand2);
}
public static XNodeParent gt(Object operand1, Object operand2) {
return build(TypeExpr.GT, operand1, operand2);
}
public static XNodeParent ne(Object... operands) {
return build(TypeExpr.NE, operands);
}
public static XNodeParent eq(Object... operands) {
return build(TypeExpr.EQ, operands);
}
public static XNode set(Object... operands) {
if (operands.length == 0)
return new XNodeLeaf(TypeExpr.SET, null);
if (operands.length == 1 && operands[0] instanceof Range)
return set(((Range) operands[0]).toArray());
if (operands.length == 1 && operands[0] instanceof Collection) {
Collection> coll = (Collection>) operands[0];
if (coll.size() == 0)
return new XNodeLeaf(TypeExpr.SET, null);
Object first = coll.iterator().next();
if (first instanceof Byte || first instanceof Short || first instanceof Integer || first instanceof Long)
return new XNodeParent(TypeExpr.SET,
coll.stream().map(s -> new XNodeLeaf(TypeExpr.LONG, ((Number) s).longValue())).collect(toList()));
if (first instanceof String)
return new XNodeParent(TypeExpr.SET, coll.stream().map(s -> new XNodeLeaf(TypeExpr.SYMBOL, s)).collect(toList()));
throw new RuntimeException();
}
return build(TypeExpr.SET, operands);
}
public static XNode set(int[] operands) {
if (operands.length == 0)
return new XNodeLeaf(TypeExpr.SET, null);
return new XNodeParent(TypeExpr.SET, IntStream.of(operands).mapToObj(v -> new XNodeLeaf(TypeExpr.LONG, (long) v)).collect(toList()));
}
public static XNodeParent in(Object var, Object set) {
return build(TypeExpr.IN, var, set);
}
public static XNodeParent notin(Object var, Object set) {
return build(TypeExpr.NOTIN, var, set);
}
public static XNodeParent not(Object operand) {
return build(TypeExpr.NOT, operand);
}
public static XNodeParent and(Object... operands) {
return operands.length == 1 ? (XNodeParent) operands[0] : build(TypeExpr.AND, operands); // modeling
// facility
}
public static XNodeParent or(Object... operands) {
return operands.length == 1 ? (XNodeParent) operands[0] : build(TypeExpr.OR, operands); // modeling
// facility
}
public static XNodeParent xor(Object... operands) {
return build(TypeExpr.XOR, operands);
}
public static XNodeParent iff(Object... operands) {
return build(TypeExpr.IFF, operands);
}
public static XNodeParent imp(Object operand1, Object operand2) {
return build(TypeExpr.IMP, operand1, operand2);
}
public static XNodeParent ifThenElse(Object operand1, Object operand2, Object operand3) {
return build(TypeExpr.IF, operand1, operand2, operand3);
}
public static XNodeParent scalar(int[] t1, Object[] t2) {
Utilities.control(t1.length == t2.length, "Not the same number of elements in the two arrays");
return new XNodeParent(TypeExpr.ADD, IntStream.range(0, t1.length).mapToObj(i -> mul(t1[i], t2[i])).collect(toList()));
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof XNodeParent))
return false;
XNodeParent> node = (XNodeParent>) obj;
return type == node.type && sons.length == node.sons.length && IntStream.range(0, sons.length).allMatch(i -> sons[i].equals(node.sons[i]));
}
@Override
public int compareTo(XNode obj) {
if (type != obj.type)
return Integer.compare(type.ordinal(), obj.type.ordinal());
XNodeParent node = (XNodeParent) obj;
if (sons.length < node.sons.length)
return -1;
if (sons.length > node.sons.length)
return 1;
return IntStream.range(0, sons.length).map(i -> sons[i].compareTo(node.sons[i])).filter(v -> v != 0).findFirst().orElse(0);
}
/** Builds a parent node for a syntactic tree, with the specified type and the specified sons. */
public XNodeParent(TypeExpr type, XNode[] sons) {
super(type, sons);
Utilities.control(sons.length > 0, "Pb with this node that should be a parent");
}
/** Builds a parent node for a syntactic tree, with the specified type and the specified sons. */
public XNodeParent(TypeExpr type, List> sons) {
this(type, sons.toArray(new XNode[sons.size()]));
}
/** Builds a parent node for a syntactic tree, with the specified type and the specified son. */
public XNodeParent(TypeExpr type, XNode son) {
this(type, Arrays.asList(son));
}
/** Builds a parent node for a syntactic tree, with the specified type and the two specified sons. */
public XNodeParent(TypeExpr type, XNode son1, XNode son2) {
this(type, Arrays.asList(son1, son2));
}
@Override
public int size() {
return 1 + Stream.of(sons).mapToInt(c -> c.size()).sum();
}
@Override
public int maxParameterNumber() {
return Stream.of(sons).mapToInt(c -> c.maxParameterNumber()).max().orElse(-1);
}
// private Matcher matcher() {
// return new Matcher((XNodeParent) this);
// }
private static class Canonizer {
private Matcher abs_sub = new Matcher(node(ABS, node(SUB, any, any)));
private Matcher not_not = new Matcher(node(NOT, node(NOT, any)));
private Matcher neg_neg = new Matcher(node(NEG, node(NEG, any)));
private Matcher any_lt_k = new Matcher(node(LT, any, val));
private Matcher k_lt_any = new Matcher(node(LT, val, any));
private Matcher not_logop = new Matcher(node(NOT, anyc), (node, level) -> level == 1 && node.type.isLogicallyInvertible());
private Matcher not_symrel_any = new Matcher(node(symop, not, any)); // , (node, level) -> level == 0 && node.type.oneOf(EQ, NE));
private Matcher any_symrel_not = new Matcher(node(symop, any, not)); // , (node, level) -> level == 0 && node.type.oneOf(EQ, NE));
private Matcher x_mul_k__eq_l = new Matcher(node(EQ, node(MUL, var, val), val));
private Matcher flattenable = new Matcher(anyc,
(node, level) -> level == 0 && node.type.oneOf(ADD, MUL, MIN, MAX, AND, OR) && Stream.of(node.sons).anyMatch(s -> s.type == node.type));
private Matcher mergeable = new Matcher(anyc, (node, level) -> level == 0 && node.type.oneOf(ADD, MUL, MIN, MAX, AND, OR) && node.sons.length >= 2
&& node.sons[node.sons.length - 1].type == LONG && node.sons[node.sons.length - 2].type == LONG);
private Matcher sub_relop_sub = new Matcher(node(relop, sub, sub));
private Matcher any_relop_sub = new Matcher(node(relop, any, sub));
private Matcher sub_relop_any = new Matcher(node(relop, sub, any));
private Matcher any_add_val__relop__any_add_val = new Matcher(node(relop, any_add_val, any_add_val));
private Matcher var_add_val__relop__val = new Matcher(node(relop, var_add_val, val));
private Matcher val__relop__var_add_val = new Matcher(node(relop, val, var_add_val));
private Matcher imp_logop = new Matcher(node(IMP, anyc, any), (node, level) -> level == 1 && node.type.isLogicallyInvertible());
private Matcher imp_not = new Matcher(node(IMP, node(NOT, any), any));
private Map, XNode>> rules = new LinkedHashMap<>();
private Canonizer() {
rules.put(abs_sub, r -> node(DIST, r.sons[0].sons)); // abs(sub(a,b)) => dist(a,b)
rules.put(not_not, r -> r.sons[0].sons[0]); // not(not(a)) => a
rules.put(neg_neg, r -> r.sons[0].sons[0]); // neg(neg(a)) => a
rules.put(any_lt_k, r -> node(LE, r.sons[0], augment(r.sons[1], -1))); // e.g., lt(x,5) => le(x,4)
rules.put(k_lt_any, r -> node(LE, augment(r.sons[0], 1), r.sons[1])); // e.g., lt(5,x) => le(6,x)
rules.put(not_logop, r -> node(r.sons[0].type.logicalInversion(), r.sons[0].sons)); // e.g., not(lt(x)) => ge(x)
rules.put(not_symrel_any, r -> node(r.type.logicalInversion(), r.sons[0].sons[0], r.sons[1])); // e.g., ne(not(x),y) => eq(x,y)
rules.put(any_symrel_not, r -> node(r.type.logicalInversion(), r.sons[0], r.sons[1].sons[0])); // e.g.,ne(x,not(y)) => eq(x,y)
rules.put(x_mul_k__eq_l, r -> r.val(1) % r.val(0) == 0 ? node(EQ, r.sons[0].sons[0], longLeaf(r.val(1) / r.val(0))) : longLeaf(0));
// below, e.g., eq(mul(x,4),8) => eq(x,2) and eq(mul(x,4),6) => 0 (false)
rules.put(flattenable, r -> { // we flatten operators when possible; for example add(add(x,y),z) becomes add(x,y,z)
int l1 = r.sons.length, pos = IntStream.range(0, l1).filter(i -> r.sons[i].type == r.type).findFirst().getAsInt(), l2 = r.sons[pos].sons.length;
Stream> list = IntStream.range(0, l1 - 1 + l2)
.mapToObj(j -> j < pos ? r.sons[j] : j < pos + l2 ? r.sons[pos].sons[j - pos] : r.sons[j - l2 + 1]);
return node(r.type, list);
});
rules.put(mergeable, r -> { // we merge long when possible. e.g., add(a,3,2) => add(a,5) and max(a,2,1) => max(a,2)
XNode[] t = Arrays.copyOf(r.sons, r.arity() - 1);
long v1 = r.sons[r.arity() - 1].val(0), v2 = r.sons[r.arity() - 2].val(0);
t[r.arity() - 2] = longLeaf(r.type == ADD ? v1 + v2 : r.type == MUL ? v1 * v2 : r.type.oneOf(MIN, AND) ? Math.min(v1, v2) : Math.max(v1, v2));
return node(r.type, t);
});
// We replace sub by add when possible
rules.put(sub_relop_sub, r -> node(r.type, node(ADD, r.sons[0].sons[0], r.sons[1].sons[1]), node(ADD, r.sons[1].sons[0], r.sons[0].sons[1])));
rules.put(any_relop_sub, r -> node(r.type, node(ADD, r.sons[0], r.sons[1].sons[1]), r.sons[1].sons[0]));
rules.put(sub_relop_any, r -> node(r.type, r.sons[0].sons[0], node(ADD, r.sons[1], r.sons[0].sons[1])));
// We remove add when possible
rules.put(any_add_val__relop__any_add_val,
r -> node(r.type, node(ADD, r.sons[0].sons[0], longLeaf(r.sons[0].sons[1].val(0) - r.sons[1].sons[1].val(0))), r.sons[1].sons[0]));
rules.put(var_add_val__relop__val, r -> node(r.type, r.sons[0].sons[0], longLeaf(r.sons[1].val(0) - r.sons[0].sons[1].val(0))));
rules.put(val__relop__var_add_val, r -> node(r.type, longLeaf(r.sons[0].val(0) - r.sons[1].sons[1].val(0)), r.sons[1].sons[0]));
// mul(x,1) = > x, and add(x,0) => x // TODO
rules.put(imp_logop, r -> node(OR, r.sons[0].logicalInversion(), r.sons[1])); // seems better to do that
rules.put(imp_not, r -> node(OR, r.sons[0].sons[0], r.sons[1]));
}
private XNode augment(XNode n, int offset) {
((XNodeLeaf>) n).value = (long) n.val(0) + offset;
return n;
}
}
private static Canonizer> canonizer;
private Canonizer canonizer() {
if (XNodeParent.canonizer != null)
return (Canonizer) XNodeParent.canonizer;
Canonizer can = new Canonizer<>();
canonizer = can;
return can;
}
@Override
public XNode canonization() {
// We will build the canonized form of the node, with the local variables type and sons
TypeExpr type = this.type; // possibly, this initial value of type will be modified during canonization
XNode[] sons = this.sons.clone();
IntStream.range(0, sons.length).forEach(i -> sons[i] = sons[i].canonization()); // sons are made canonical
if (type.isSymmetricOperator())
Arrays.sort(sons); // Sons are sorted if the type of the node is symmetric
// Now, sons are potentially sorted if the type corresponds to a non-symmetric binary relational operator (in
// that case, we swap sons and
// arithmetically inverse the operator provided that the ordinal value of the reverse operator is smaller)
if (sons.length == 2 && type.isUnsymmetricRelationalOperator() && (type.arithmeticInversion().ordinal() < type.ordinal()
|| (type.arithmeticInversion().ordinal() == type.ordinal() && sons[0].compareTo(sons[1]) > 0))) {
type = type.arithmeticInversion();
Utilities.swap(sons, 0, 1);
}
if (sons.length == 1 && type.isIdentityWhenOneOperand()) // add(x) becomes x, min(x) becomes x, ...
return sons[0]; // certainly can happen during the canonization process
XNodeParent node = node(type, sons);
Entry, XNode>> rule = canonizer().rules.entrySet().stream().filter(e -> e.getKey().matches(node)).findFirst()
.orElse(null);
return rule != null ? rule.getValue().apply(node).canonization() : node;
}
private XNode buildNewTreeUsing(Function, XNode> f) {
return new XNodeParent(type, Stream.of(sons).map(f).collect(Collectors.toList()));
}
@Override
public XNode abstraction(List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy