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

fr.boreal.backward_chaining.source_target.dlx.DLX Maven / Gradle / Ivy

Go to download

InteGraal has been designed in a modular way, in order to facilitate software reuse and extension. It should make it easy to test new scenarios and techniques, in particular by combining algorithms. The main features of Graal are currently the following: (1) internal storage to store data by using a SQL or RDF representation (Postgres, MySQL, HSQL, SQLite, Remote SPARQL endpoints, Local in-memory triplestores) as well as a native in-memory representation (2) data-integration capabilities for exploiting federated heterogeneous data-sources through mappings able to target systems such as SQL, RDF, and black-box (e.g. Web-APIs) (3) algorithms for query-answering over heterogeneous and federated data based on query rewriting and/or forward chaining (or chase)

There is a newer version: 1.5.1
Show newest version
package fr.boreal.backward_chaining.source_target.dlx;

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

import fr.boreal.backward_chaining.source_target.dlx.data.ColumnObject;
import fr.boreal.backward_chaining.source_target.dlx.data.DataObject;
import fr.boreal.backward_chaining.source_target.dlx.data.DebugDataObject;

/**
 * Knuth's DLX algorithm.  This code implements a solution to the exact
 * matrix cover problem.  It works particularly well on large problems.
 * 

* The code presented is a straightforward translation of the pseudocode * supplied for the algorithm in the paper; use the paper as a starting point * for understanding the code. *

* See Knuth, D.; "Dancing Links", Stanford University, 2000. */ public class DLX { /** * Build the DLX data structure from a byte matrix of ones and zeroes. * @param input a list of rows of bytes. All rows must be same size. * @return root node of initialized data structure for DLX algorithm */ public static ColumnObject buildSparseMatrix(byte[][] input) { return buildSparseMatrix(input, null, false); } /** * Build the DLX data structure from a byte matrix of ones and zeroes. * @param input a list of rows of bytes. All rows must be same size. * @param labels column labels * @return root node of initialized data structure for DLX algorithm */ public static ColumnObject buildSparseMatrix(byte[][] input, Object[] labels) { return buildSparseMatrix(input, labels, false); } /** * Build the DLX data structure from a byte matrix of ones and zeroes. * @param input a list of rows of bytes. All rows must be same size. * @param labels column labels * @param dbgUseRowSquenceNumbers annotate data objects with a sequential * row number for debugging and testing purposes. Set to * false for production use. * @return root node of initialized data structure for DLX algorithm */ public static ColumnObject buildSparseMatrix(byte[][] input, Object[] labels, boolean dbgUseRowSquenceNumbers) { // Make root node and column headers final ColumnObject h = new ColumnObject(); h.L = h; h.R = h; h.U = h; h.D = h; for (int j = 0; j < input[0].length; ++j) { final ColumnObject newColHdr = new ColumnObject(); newColHdr.U = newColHdr; newColHdr.D = newColHdr; newColHdr.R = h; newColHdr.L = h.L; h.L.R = newColHdr; h.L = newColHdr; if ((labels != null) && (j < labels.length) && (labels[j] != null)) newColHdr.name = labels[j]; } // For each row, build a list and stitch it onto the column headers long rowSequenceNumber = 0; final List thisRowObjects = new LinkedList<>(); for (byte[] bytes : input) { thisRowObjects.clear(); ColumnObject currentCol = (ColumnObject) h.R; for (byte aByte : bytes) { // Build data objects, attach to column objects if (aByte != 0) { DataObject obj; if (dbgUseRowSquenceNumbers) { obj = new DebugDataObject(); ((DebugDataObject) obj).rowSeqNum = rowSequenceNumber; } else { obj = new DataObject(); } obj.U = currentCol.U; obj.D = currentCol; obj.L = obj; obj.R = obj; obj.C = currentCol; currentCol.U.D = obj; currentCol.U = obj; currentCol.size++; thisRowObjects.add(obj); } currentCol = (ColumnObject) currentCol.R; } // Link all data objects built for this row horizontally if (!thisRowObjects.isEmpty()) { final Iterator iter = thisRowObjects.iterator(); final DataObject first = iter.next(); while (iter.hasNext()) { final DataObject thisObj = iter.next(); thisObj.L = first.L; thisObj.R = first; first.L.R = thisObj; first.L = thisObj; } } rowSequenceNumber++; } return h; } /** * Given the supplied sparse matrix, run Knuth's DLX algorithm over it, * sending the results to the console. It is highly advisable that when * using this method, you build the sparse matrix with a meaningful set * of labels, in order to get proper output. * * @param h the root of the sparse matrix to solve * @param useSHeuristic flag specifying whether to attempt to * minimize the depth of the search tree by picking columns to cover * that contain the least ones in it */ public static void solve(ColumnObject h, boolean useSHeuristic) { solve(h, useSHeuristic, new SimpleDLXResultProcessor()); } /** * Given the supplied sparse matrix, run Knuth's DLX algorithm over it, * passing solutions to resultProcessor to process. * * @param h the root of the sparse matrix to solve * @param useSHeuristic flag specifying whether to attempt to * minimize the depth of the search tree by picking columns to cover * that contain the least ones in it * @param resultProcessor an object which supplies result processing * strategy */ public static void solve(ColumnObject h, boolean useSHeuristic, DLXResultProcessor resultProcessor) { solve(h, useSHeuristic, resultProcessor, 0, new ArrayList<>()); } /** * Reconfigure the sparse matrix to make a number of columns optional. * By 'optional', we mean that a one may appear at most once in the solved * matrix, as opposed to the usual case where a one must appear once and * only once. The sum of numMandatory and * numOptional must add up to the exact number of columns in * the matrix. If not, a SudokuException is thrown. * * @param h the root node of the sparse matrix * @param numMandatory the number of columns which are mandatory (always * columns 0 to (numMandatory - 1)) * @param numOptional the number of optional columns (always the last * numOptional columns) */ public static void setColumnsAsOptional(ColumnObject h, int numMandatory, int numOptional) { if (numMandatory < 0) throw new IllegalArgumentException("numMandatory"); if (numOptional < 1) throw new IllegalArgumentException("numOptional"); if (h == null) throw new IllegalArgumentException("total"); int total = numMandatory + numOptional; DataObject[] columns = new DataObject[total]; DataObject current = h.R; for (int i = 0; i < total; ++ i) { columns[i] = current; if (current == h) { // root seen too early, complain throw new DLXException("Expected at least " + total + " columns"); } current = current.R; } // Wrap mandatory columns if (numMandatory > 0) { columns[numMandatory - 1].R = h; h.L = columns[numMandatory - 1]; } // Optional columns' L and R references point to itself for (int i = numMandatory; i < total; ++ i) { columns[i].L = columns[i]; columns[i].R = columns[i]; } } static boolean solve(ColumnObject h, boolean useSHeuristic, DLXResultProcessor resultProcessor, int k, List o) { if (h.R == h) { return processResult(resultProcessor, o); } ColumnObject c = (ColumnObject)h.R; if (useSHeuristic) { int s = Integer.MAX_VALUE; ColumnObject j = ((ColumnObject)h.R); while (j != h) { if (j.size < s) { s = j.size; c = j; } j = (ColumnObject)j.R; } } boolean stillRunning = true; cover(h, c); DataObject r = c.D; while ((stillRunning) && (r != c)) { grow(o, k+1); o.set(k, r); DataObject j = r.R; while (j != r) { cover(h, (ColumnObject)j.C); j = j.R; } stillRunning = solve(h, useSHeuristic, resultProcessor, k+1, o); j = r.L; while (j != r) { uncover(h, (ColumnObject)j.C); j = j.L; } r = r.D; } uncover(h, c); return (stillRunning); } private static void grow(List o, int k) { if (o.size() < k) { while (o.size() < k) { o.add(null); } } else if (o.size() > k) { while (o.size() > k) { o.removeLast(); } } } private static boolean processResult(DLXResultProcessor processor, List o) { final List> resultSet = new LinkedList<>(); for (final DataObject oK : o) { final List resultRow = new LinkedList<>(); DataObject node = oK; do { resultRow.add(((ColumnObject)node.C).name); node = node.R; } while (node != oK); resultSet.add(resultRow); } final DLXResult result = new DLXResult(resultSet); return processor.processResult(result); } /** * Cover a column in the sparse matrix * @param h the root of the sparse matrix * @param columnObj the column header of the column to cover */ static void cover(ColumnObject h, ColumnObject columnObj) { columnObj.R.L = columnObj.L; columnObj.L.R = columnObj.R; DataObject i = columnObj.D; while (i != columnObj) { DataObject j = i.R; while (j != i) { j.D.U = j.U; j.U.D = j.D; ((ColumnObject)j.C).size--; j = j.R; } i = i.D; } } /** * Uncovers a column in the sparse matrix * @param h the root of the sparse matrix * @param columnObj the column header of the column to uncover. It must * have been covered by a prior * {@link #cover(ColumnObject, ColumnObject)} operation. */ static void uncover(ColumnObject h, ColumnObject columnObj) { DataObject i = columnObj.U; while (i != columnObj) { DataObject j = i.L; while (j != i) { ((ColumnObject)j.C).size++; j.D.U = j; j.U.D = j; j = j.L; } i = i.U; } columnObj.R.L = columnObj; columnObj.L.R = columnObj; } }