Commit 046133ff authored by Charles Vernerey's avatar Charles Vernerey
Browse files

Separate Overlap constraint from PropOverlap propagator class

parent 3bd3f15e
Loading
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -88,7 +88,7 @@ loadScripts(document, 'script');</script>
</div>
<div class="inheritance" title="Inheritance Tree"><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/Object.html" title="class or interface in java.lang" class="external-link">java.lang.Object</a>
<div class="inheritance"><a href="https://javadoc.io/doc/org.choco-solver/choco-solver/4.10.13/org.chocosolver.solver/org/chocosolver/solver/constraints/Propagator.html" title="class or interface in org.chocosolver.solver.constraints" class="external-link">org.chocosolver.solver.constraints.Propagator</a>&lt;<a href="https://javadoc.io/doc/org.choco-solver/choco-solver/4.10.13/org.chocosolver.solver/org/chocosolver/solver/variables/IntVar.html" title="class or interface in org.chocosolver.solver.variables" class="external-link">IntVar</a>&gt;
<div class="inheritance">io.gitlab.chaver.mining.patterns.constraints.Overlap</div>
<div class="inheritance">io.gitlab.chaver.mining.patterns.constraints.PropOverlap</div>
</div>
</div>
<section class="class-description" id="class-description">
+3 −3
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@
<html lang="en">
<head>
<!-- Generated by javadoc (20) on Fri Jul 21 11:54:49 CEST 2023 -->
<title>Uses of Class io.gitlab.chaver.mining.patterns.constraints.Overlap (io.gitlab.chaver:data-mining 1.0.2 API)</title>
<title>Uses of Class io.gitlab.chaver.mining.patterns.constraints.PropOverlap (io.gitlab.chaver:data-mining 1.0.2 API)</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="dc.created" content="2023-07-21">
@@ -49,9 +49,9 @@ loadScripts(document, 'script');</script>
<div class="flex-content">
<main role="main">
<div class="header">
<h1 title="Uses of Class io.gitlab.chaver.mining.patterns.constraints.Overlap" class="title">Uses of Class<br>io.gitlab.chaver.mining.patterns.constraints.Overlap</h1>
<h1 title="Uses of Class io.gitlab.chaver.mining.patterns.constraints.PropOverlap" class="title">Uses of Class<br>io.gitlab.chaver.mining.patterns.constraints.PropOverlap</h1>
</div>
No usage of io.gitlab.chaver.mining.patterns.constraints.Overlap</main>
No usage of io.gitlab.chaver.mining.patterns.constraints.PropOverlap</main>
<footer role="contentinfo">
<hr>
<p class="legal-copy"><small>Copyright &#169; 2023. All rights reserved.</small></p>
+3 −4
Original line number Diff line number Diff line
@@ -15,7 +15,6 @@ import io.gitlab.chaver.mining.patterns.io.DatReader;
import io.gitlab.chaver.mining.patterns.io.TransactionalDatabase;
import org.chocosolver.solver.Model;
import org.chocosolver.solver.Solver;
import org.chocosolver.solver.constraints.Constraint;
import org.chocosolver.solver.search.strategy.Search;
import org.chocosolver.solver.search.strategy.selectors.values.IntDomainMin;
import org.chocosolver.solver.search.strategy.selectors.variables.InputOrder;
@@ -44,10 +43,10 @@ public class ExampleDiversity {
        double jmax = 0.05;
        // Overlap is a global constraint that ensures that x is a diverse itemset
        // i.e. there exists no y in the history such that jaccard(x,y) > jmax
        Overlap overlap = new Overlap(database, x, jmax, theta);
        model.post(new Constraint("Overlap", overlap));
        // Each time a new itemset x is found, we add it to the history if it is a diverse itemset
        Overlap overlap = ConstraintFactory.overlap(database, x, jmax, theta);
        overlap.post();
        Solver solver = model.getSolver();
        solver.plugMonitor(overlap);
        solver.setSearch(Search.intVarSearch(
                new InputOrder<>(model),
                new IntDomainMin(),
+22 −77
Original line number Diff line number Diff line
@@ -10,41 +10,37 @@
package io.gitlab.chaver.mining.patterns.constraints;

import io.gitlab.chaver.mining.patterns.io.TransactionalDatabase;
import org.chocosolver.solver.constraints.Propagator;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.constraints.Constraint;
import org.chocosolver.solver.search.loop.monitors.IMonitorSolution;
import org.chocosolver.solver.variables.BoolVar;
import org.chocosolver.solver.variables.IntVar;
import org.chocosolver.util.ESat;

import java.util.*;
import java.util.BitSet;
import java.util.List;
import java.util.stream.IntStream;

/**
 * A constraint inspired by ClosedDiversity (see Hien et al. - A Relaxation-based Approach for Mining Diverse Closed Patterns)
 * Given a transactional database, a vector of boolean variables x, a jaccard diversity threshold jmax and
 * a min frequency threshold theta, ensures that there exists no itemset y in the history such that
 * jaccard(x,y) &gt; jmax
 * A constraint wrapper for the constraint Overlap to access itemsets and covers history, and automatically plug the
 * solution monitor.
 */
public class Overlap extends Propagator<IntVar> implements IMonitorSolution {
public class Overlap extends Constraint implements IMonitorSolution {

    private TransactionalDatabase database;
    private BitSet[] verticalRepresentation;
    private BoolVar[] x;
    private double jmax;
    private int theta;
    /** History of encountered itemsets */
    private List<int[]> itemsetsHistory = new ArrayList<>();
    /** History of the cover of each itemset */
    private List<BitSet> coversHistory = new ArrayList<>();
    private PropOverlap propOverlap;

    public Overlap(TransactionalDatabase database, BoolVar[] x, double jmax, int theta) {
        super(x);
        super("Overlap", new PropOverlap(database, x, jmax, theta));
        this.database = database;
        this.verticalRepresentation = database.getVerticalRepresentation();
        this.x = x;
        this.jmax = jmax;
        this.theta = theta;
        this.propOverlap = (PropOverlap) getPropagators()[0];
        // Plug this search monitor to check if the solution itemset is diverse w.r.t. the history before adding it
        x[0].getModel().getSolver().plugMonitor(this);
    }

    private BitSet createCover() {
@@ -53,57 +49,6 @@ public class Overlap extends Propagator<IntVar> implements IMonitorSolution {
        return cover;
    }

    private BitSet computeCoverUnion(BitSet cover, BitSet cover2) {
        BitSet coverUnion = (BitSet) cover.clone();
        coverUnion.and(cover2);
        return coverUnion;
    }

    private double LBJaccard(BitSet xCover, BitSet HCover) {
        BitSet inter = (BitSet) xCover.clone();
        inter.and(HCover);
        int properCoverCardinality = xCover.cardinality() - inter.cardinality();
        return (double) (theta - properCoverCardinality) / (xCover.cardinality() + HCover.cardinality() + properCoverCardinality - theta);
        //return (double) (theta - (properCoverCardinality)) / Math.min(xCover.cardinality(), HCover.cardinality());
    }

    private boolean PGrowthLB(BitSet xCover) {
        for (BitSet HCover : coversHistory) {
            if (LBJaccard(xCover, HCover) > jmax) {
                return false;
            }
        }
        return true;
    }

    @Override
    public void propagate(int evtmask) throws ContradictionException {
        BitSet xCover = createCover();
        Set<Integer> freeItems = new HashSet<>();
        for (int i = 0; i < database.getNbItems(); i++) {
            if (x[i].isInstantiatedTo(1)) {
                xCover.and(verticalRepresentation[i]);
            }
            if (!x[i].isInstantiated()) {
                freeItems.add(i);
            }
        }
        // Fails if x+ is not diversified
        if (!PGrowthLB(xCover)) {
            fails();
        }
        for (int i : freeItems) {
            if (!PGrowthLB(computeCoverUnion(xCover, verticalRepresentation[i]))) {
                x[i].setToFalse(this);
            }
        }
    }

    @Override
    public ESat isEntailed() {
        return ESat.UNDEFINED;
    }

    @Override
    public void onSolution() {
        int[] itemset = IntStream
@@ -114,21 +59,13 @@ public class Overlap extends Propagator<IntVar> implements IMonitorSolution {
        for (int i : itemset) {
            cover.and(verticalRepresentation[i]);
        }
        for (BitSet HCover : coversHistory) {
        for (BitSet HCover : propOverlap.getCoversHistory()) {
            if (computeJaccard(cover, HCover) > jmax) {
                return;
            }
        }
        itemsetsHistory.add(itemset);
        coversHistory.add(cover);
    }

    public List<int[]> getItemsetsHistory() {
        return itemsetsHistory;
    }

    public List<BitSet> getCoversHistory() {
        return coversHistory;
        propOverlap.getItemsetsHistory().add(itemset);
        propOverlap.getCoversHistory().add(cover);
    }

    public static double computeJaccard(BitSet cov, BitSet cov2) {
@@ -138,4 +75,12 @@ public class Overlap extends Propagator<IntVar> implements IMonitorSolution {
        union.or(cov2);
        return (double) inter.cardinality() / union.cardinality();
    }

    public List<int[]> getItemsetsHistory() {
        return propOverlap.getItemsetsHistory();
    }

    public List<BitSet> getCoversHistory() {
        return propOverlap.getCoversHistory();
    }
}
+112 −0
Original line number Diff line number Diff line
/*
 * This file is part of io.gitlab.chaver:data-mining (https://gitlab.com/chaver/data-mining)
 *
 * Copyright (c) 2023, IMT Atlantique
 *
 * Licensed under the MIT license.
 *
 * See LICENSE file in the project root for full license information.
 */
package io.gitlab.chaver.mining.patterns.constraints;

import io.gitlab.chaver.mining.patterns.io.TransactionalDatabase;
import org.chocosolver.solver.constraints.Propagator;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.variables.BoolVar;
import org.chocosolver.solver.variables.IntVar;
import org.chocosolver.util.ESat;

import java.util.*;

/**
 * A constraint inspired by ClosedDiversity (see Hien et al. - A Relaxation-based Approach for Mining Diverse Closed Patterns)
 * Given a transactional database, a vector of boolean variables x, a jaccard diversity threshold jmax and
 * a min frequency threshold theta, ensures that there exists no itemset y in the history such that
 * jaccard(x,y) &gt; jmax
 */
public class PropOverlap extends Propagator<IntVar> {

    private TransactionalDatabase database;
    private BitSet[] verticalRepresentation;
    private BoolVar[] x;
    private double jmax;
    private int theta;
    /** History of encountered itemsets */
    private List<int[]> itemsetsHistory = new ArrayList<>();
    /** History of the cover of each itemset */
    private List<BitSet> coversHistory = new ArrayList<>();

    public PropOverlap(TransactionalDatabase database, BoolVar[] x, double jmax, int theta) {
        super(x);
        this.database = database;
        this.verticalRepresentation = database.getVerticalRepresentation();
        this.x = x;
        this.jmax = jmax;
        this.theta = theta;
    }

    private BitSet createCover() {
        BitSet cover = new BitSet(database.getNbTransactions());
        cover.set(0, database.getNbTransactions());
        return cover;
    }

    private BitSet computeCoverUnion(BitSet cover, BitSet cover2) {
        BitSet coverUnion = (BitSet) cover.clone();
        coverUnion.and(cover2);
        return coverUnion;
    }

    private double LBJaccard(BitSet xCover, BitSet HCover) {
        BitSet inter = (BitSet) xCover.clone();
        inter.and(HCover);
        int properCoverCardinality = xCover.cardinality() - inter.cardinality();
        return (double) (theta - properCoverCardinality) / (xCover.cardinality() + HCover.cardinality() + properCoverCardinality - theta);
        //return (double) (theta - (properCoverCardinality)) / Math.min(xCover.cardinality(), HCover.cardinality());
    }

    private boolean PGrowthLB(BitSet xCover) {
        for (BitSet HCover : coversHistory) {
            if (LBJaccard(xCover, HCover) > jmax) {
                return false;
            }
        }
        return true;
    }

    @Override
    public void propagate(int evtmask) throws ContradictionException {
        BitSet xCover = createCover();
        Set<Integer> freeItems = new HashSet<>();
        for (int i = 0; i < database.getNbItems(); i++) {
            if (x[i].isInstantiatedTo(1)) {
                xCover.and(verticalRepresentation[i]);
            }
            if (!x[i].isInstantiated()) {
                freeItems.add(i);
            }
        }
        // Fails if x+ is not diversified
        if (!PGrowthLB(xCover)) {
            fails();
        }
        for (int i : freeItems) {
            if (!PGrowthLB(computeCoverUnion(xCover, verticalRepresentation[i]))) {
                x[i].setToFalse(this);
            }
        }
    }

    @Override
    public ESat isEntailed() {
        return ESat.UNDEFINED;
    }

    public List<int[]> getItemsetsHistory() {
        return itemsetsHistory;
    }

    public List<BitSet> getCoversHistory() {
        return coversHistory;
    }
}
Loading