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

io.gitlab.chaver.mining.patterns.constraints.Generator Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
/*
 * This file is part of io.gitlab.chaver:data-mining (https://gitlab.com/chaver/data-mining)
 *
 * Copyright (c) 2022, 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.Database;
import io.gitlab.chaver.mining.patterns.util.BitSetFacade;
import io.gitlab.chaver.mining.patterns.util.ConstraintSettings;
import io.gitlab.chaver.mining.patterns.util.SparseBitSet;
import org.chocosolver.memory.IStateInt;
import org.chocosolver.solver.constraints.Propagator;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.variables.BoolVar;
import org.chocosolver.solver.variables.events.IntEventType;
import org.chocosolver.util.ESat;

import java.util.HashMap;
import java.util.Map;
import java.util.stream.IntStream;

import static io.gitlab.chaver.mining.patterns.util.BitSetFactory.getBitSet;


/**
 * Given a set of boolean variables x, ensures that x is a generator
 * (i.e. there exists no item i \in x such that freq(x \ {i}) = freq(x))
 * Fore more information, see Belaid et al. - Constraint programming for association rules
 */
public class Generator extends Propagator {

    private final BoolVar[] items;
    private final Database database;
    private final long[][] dataset;
    private final BitSetFacade cover;
    private final Map subCovers = new HashMap<>();
    private final int[] freeItems;
    private final IStateInt lastIndexFree;
    private final int[] presentItems;
    private final IStateInt lastIndexPresent;
    private final int firstIndex;

    public Generator(Database database, BoolVar[] items) {
        super(items);
        this.items = items;
        this.database = database;
        this.dataset = database.getDatasetAsLongArray();
        this.cover = getBitSet(ConstraintSettings.BITSET_TYPE, database, getModel());
        this.freeItems = IntStream.range(0, database.getNbItems()).toArray();
        this.lastIndexFree = getModel().getEnvironment().makeInt(items.length);
        this.firstIndex = database.getNbClass();
        this.presentItems = freeItems.clone();
        this.lastIndexPresent = getModel().getEnvironment().makeInt(firstIndex);
    }

    @Override
    public void propagate(int evtmask) throws ContradictionException {
        int nFree = lastIndexFree.get();
        int nPres = lastIndexPresent.get();
        // Compute cover and free/present items
        for (int i = nFree - 1; i >= firstIndex; i--) {
            int idx = freeItems[i];
            if (items[idx].isInstantiated()) {
                nFree = removeItem(i, nFree, idx);
                if (items[idx].isInstantiatedTo(1)) {
                    nPres = addItem(nPres, idx);
                    cover.and(idx);
                }
            }
        }
        // compute subcovers
        int coverSize = cover.cardinality();
        for (int j = nPres - 1; j >= firstIndex ; j--) {
            int idx = presentItems[j];
            subCovers.put(idx, new SparseBitSet(database.getNbTransactions()));
            SparseBitSet subCover = subCovers.get(idx);
            for (int i = nPres - 1; i >= firstIndex ; i--) {
                if (j != i) {
                    int idx2 = presentItems[i];
                    subCover.and(dataset[idx2]);
                }
            }
            if (subCover.cardinality() == coverSize) fails();
        }
        // remove all items that do not lead to a generator
        for (int i = nFree - 1; i >= firstIndex; i--) {
            int idx = freeItems[i];
            if (isGenerator(idx, coverSize, nPres)) {
                nFree = removeItem(i, nFree, idx);
                items[idx].setToFalse(this);
            }
        }
        lastIndexFree.set(nFree);
        lastIndexPresent.set(nPres);
    }

    private boolean isGenerator(int idx, int coverSize, int nP) {
        int intersectionSize = cover.andCount(idx);
        if (coverSize == intersectionSize) {
            return true;
        }
        for (int j = firstIndex; j < nP; j++) {
            if (subCovers.get(presentItems[j]).andCount(dataset[idx]) == intersectionSize) return true;
        }
        return false;
    }

    private int removeItem(int i, int nU, int idx) {
        int lastU = nU - 1;
        freeItems[i] = freeItems[lastU];
        freeItems[lastU] = idx;
        return lastU;
    }

    private int addItem(int nPos, int idx) {
        presentItems[nPos] = idx;
        return nPos + 1;
    }

    @Override
    public int getPropagationConditions(int vIdx) {
        return IntEventType.combine(IntEventType.INCLOW);
    }

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy