import { JavaObject, cast_java_lang_Object } from '../../../java/lang/JavaObject'; import { JavaInteger, cast_java_lang_Integer } from '../../../java/lang/JavaInteger'; import { AVLSet, cast_de_nrw_schule_svws_core_adt_set_AVLSet } from '../../../core/adt/set/AVLSet'; import { Variable, cast_de_nrw_schule_svws_core_kursblockung_satsolver_Variable } from '../../../core/kursblockung/satsolver/Variable'; import { SatSolverA, cast_de_nrw_schule_svws_core_kursblockung_satsolver_SatSolverA } from '../../../core/kursblockung/satsolver/SatSolverA'; import { NullPointerException, cast_java_lang_NullPointerException } from '../../../java/lang/NullPointerException'; import { LinkedCollection, cast_de_nrw_schule_svws_core_adt_collection_LinkedCollection } from '../../../core/adt/collection/LinkedCollection'; import { Clause, cast_de_nrw_schule_svws_core_kursblockung_satsolver_Clause } from '../../../core/kursblockung/satsolver/Clause'; import { JavaString, cast_java_lang_String } from '../../../java/lang/JavaString'; import { Arrays, cast_java_util_Arrays } from '../../../java/util/Arrays'; import { Heap, cast_de_nrw_schule_svws_core_kursblockung_satsolver_Heap } from '../../../core/kursblockung/satsolver/Heap'; import { System, cast_java_lang_System } from '../../../java/lang/System'; export class SatSolver3 extends SatSolverA { private static readonly MAX_LEARNED_CLAUSE_SIZE : number = 3; private heap : Heap; private vSize : number = 0; private vArrayPos : Array; private vArrayNeg : Array; private cSize : number = 0; private learnClauseMin : number = 0; /** * Konstruktor. */ public constructor() { super(); this.heap = new Heap(); this.vSize = 0; this.cSize = 0; this.vArrayPos = Array(1).fill(null); this.vArrayNeg = Array(1).fill(null); } public isVarTrue(pVar : number) : boolean { let v : Variable = this.getVarOf(pVar); return v.index === -1; } public createNewVar() : number { this.vSize++; if (this.vSize >= this.vArrayPos.length) { let newSize : number = this.vSize * 2; this.vArrayPos = Arrays.copyOf(this.vArrayPos, newSize); this.vArrayNeg = Arrays.copyOf(this.vArrayNeg, newSize); } let vPos : Variable = new Variable(+this.vSize); let vNeg : Variable = new Variable(-this.vSize); vPos.negation = vNeg; vNeg.negation = vPos; this.vArrayPos[this.vSize] = vPos; this.vArrayNeg[this.vSize] = vNeg; this.heap.insert(vPos); this.heap.insert(vNeg); return this.vSize; } public addClause(pVars : Array) : void { if (pVars.length === 0) { console.log(JSON.stringify("WARNUNG: Leere Klausel bei SatSolver.addClause(int[] pVars)!")); return; } let set : AVLSet = new AVLSet(); for (let v of pVars) { if (set.contains(-v)) { return; } set.add(v); } let list : LinkedCollection = new LinkedCollection(); while (!set.isEmpty()) { list.addLast(set.pollFirst()); } while (list.size() > 3) { let x : number = list.removeFirst().valueOf(); let y : number = list.removeFirst().valueOf(); let z : number = this.createNewVar(); list.addLast(z); this.addClause2(-x, z); this.addClause2(-y, z); this.addClause3(x, y, -z); } if (list.size() === 3) { this.addClause3(list.removeFirst().valueOf(), list.removeFirst().valueOf(), list.removeFirst().valueOf()); } if (list.size() === 2) { this.addClause2(list.removeFirst().valueOf(), list.removeFirst().valueOf()); } if (list.size() === 1) { this.addClause1(list.removeFirst().valueOf()); } } /** * Fügt der Datenstruktur eine 1-CNF-Klausel hinzu. * * @param x Das 1. Literal (Variablennummer) der Klausel. */ private addClause1(x : number) : void { let varX : Variable = this.getVarOf(x); if (varX.negation === null) throw new NullPointerException() this.heap.remove(varX); this.heap.remove(varX.negation); let c : Clause = new Clause(varX); this.cSize++; varX.clauses.addLast(c); varX.statSatFree[c.sat][c.free]++; this.heap.insert(varX); this.heap.insert(varX.negation); } /** * Fügt der Datenstruktur eine 2-CNF-Klausel hinzu. * * @param x Das 1. Literal (Variablennummer) der Klausel. * @param y Das 2. Literal (Variablennummer) der Klausel. */ private addClause2(x : number, y : number) : void { let varX : Variable = this.getVarOf(x); let varY : Variable = this.getVarOf(y); if ((varX.negation === null) || (varY.negation === null)) throw new NullPointerException() this.heap.remove(varX); this.heap.remove(varY); this.heap.remove(varX.negation); this.heap.remove(varY.negation); let c : Clause = new Clause(varX, varY); this.cSize++; varX.clauses.addLast(c); varX.statSatFree[c.sat][c.free]++; varX.neighbours.addLast(varY); varY.clauses.addLast(c); varY.statSatFree[c.sat][c.free]++; varY.neighbours.addLast(varX); this.heap.insert(varX); this.heap.insert(varY); this.heap.insert(varX.negation); this.heap.insert(varY.negation); } /** * Fügt der Datenstruktur eine 3-CNF-Klausel hinzu. * * @param x Das 1. Literal (Variablennummer) der Klausel. * @param y Das 2. Literal (Variablennummer) der Klausel. * @param z Das 3. Literal (Variablennummer) der Klausel. */ private addClause3(x : number, y : number, z : number) : void { let varX : Variable = this.getVarOf(x); let varY : Variable = this.getVarOf(y); let varZ : Variable = this.getVarOf(z); if ((varX.negation === null) || (varY.negation === null) || (varZ.negation === null)) throw new NullPointerException() this.heap.remove(varX); this.heap.remove(varY); this.heap.remove(varZ); this.heap.remove(varX.negation); this.heap.remove(varY.negation); this.heap.remove(varZ.negation); let c : Clause = new Clause(varX, varY, varZ); this.cSize++; varX.clauses.addLast(c); varX.statSatFree[c.sat][c.free]++; varX.neighbours.addLast(varY); varX.neighbours.addLast(varZ); varY.clauses.addLast(c); varY.statSatFree[c.sat][c.free]++; varY.neighbours.addLast(varX); varY.neighbours.addLast(varZ); varZ.clauses.addLast(c); varZ.statSatFree[c.sat][c.free]++; varZ.neighbours.addLast(varX); varZ.neighbours.addLast(varY); this.heap.insert(varX); this.heap.insert(varY); this.heap.insert(varZ); this.heap.insert(varX.negation); this.heap.insert(varY.negation); this.heap.insert(varZ.negation); } public getVarCount() : number { return this.vSize; } public getClauseCount() : number { return this.cSize; } public solve(pMaxTimeMillis : number) : number { let timeStart : number = System.currentTimeMillis(); let backtrackV : Array = Array(this.vSize).fill(null); let backtrackLearn : Array = Array(SatSolver3.MAX_LEARNED_CLAUSE_SIZE).fill(null); let backtrackB : Array = Array(this.vSize).fill(false); let index : number = 0; let max : number = 1; let countDown : number = 0; this.learnClauseMin = SatSolver3.MAX_LEARNED_CLAUSE_SIZE + 1; let maxIndex : number = 0; while ((index >= 0) && !this.heap.isEmpty()) { if (index > maxIndex) { maxIndex = index; } if (countDown === 0) { for (let i : number = index; i >= 0; i--){ if (backtrackV[i] !== null) { this.unitpropagation_undo(cast_de_nrw_schule_svws_core_kursblockung_satsolver_Variable(backtrackV[i])); } backtrackV[i] = null; backtrackB[i] = false; } index = 0; if ((this.learnClauseMin >= 1) && (this.learnClauseMin <= SatSolver3.MAX_LEARNED_CLAUSE_SIZE)) { let clause : Array = Array(this.learnClauseMin).fill(0); for (let i : number = 0; i < clause.length; i++){ clause[i] = -backtrackLearn[i].nr; } this.addClause(clause); } let result : number = this.simplify(); if (result !== SatSolverA.RESULT_UNKNOWN) { return result; } max = max + 1 + Math.trunc(max / 2); countDown = max; this.learnClauseMin = SatSolver3.MAX_LEARNED_CLAUSE_SIZE + 1; if (System.currentTimeMillis() - timeStart > pMaxTimeMillis) { return SatSolverA.RESULT_UNKNOWN; } continue; } countDown--; let varP : Variable | null = backtrackV[index]; if ((varP !== null) && (backtrackB[index])) { this.unitpropagation_undo(varP); backtrackV[index] = null; backtrackB[index] = false; this.learnClause(backtrackV, backtrackLearn, index); index--; continue; } if (varP === null) { varP = this.heap.top(); if (!varP.isUnsat()) { backtrackV[index] = varP; this.unitpropagation(varP); index++; continue; } } else { this.unitpropagation_undo(varP); } if (varP.negation === null) throw new NullPointerException() varP = varP.negation; if (varP.isUnsat()) { backtrackV[index] = null; this.learnClause(backtrackV, backtrackLearn, index); index--; continue; } backtrackV[index] = varP; backtrackB[index] = true; this.unitpropagation(varP); index++; continue; } return this.heap.isEmpty() ? SatSolverA.RESULT_SATISFIABLE : SatSolverA.RESULT_UNSATISFIABLE; } private learnClause(backtrackV : Array, backtrackLearn : Array, size : number) : void { if (size < 1) { return; } if (size >= this.learnClauseMin) { return; } this.learnClauseMin = size; for (let i : number = 0; i < size; i++){ backtrackLearn[i] = backtrackV[i]; } } private simplify() : number { let count1 : number = 0; let changed : boolean = true; while (changed) { changed = false; for (let nr : number = 1; nr <= this.vSize; nr++){ let varP : Variable | null = this.vArrayPos[nr]; if (varP.index < 0) { continue; } if (varP.statSatFree[0][0] > 0) { return SatSolverA.RESULT_UNSATISFIABLE; } if (varP.negation === null) throw new NullPointerException() if (varP.negation.statSatFree[0][0] > 0) { return SatSolverA.RESULT_UNSATISFIABLE; } if ((varP.statSatFree[0][1] > 0) && (varP.negation.statSatFree[0][1] > 0)) { return SatSolverA.RESULT_UNSATISFIABLE; } if (varP.statSatFree[0][1] > 0) { this.unitpropagation(varP); count1++; changed = true; continue; } if (varP.negation.statSatFree[0][1] > 0) { this.unitpropagation(varP.negation); count1++; changed = true; continue; } if (varP.getClauseOccurences() === 0) { this.unitpropagation(varP.negation); count1++; changed = true; continue; } if (varP.negation.getClauseOccurences() === 0) { this.unitpropagation(varP); count1++; changed = true; continue; } } } return SatSolverA.RESULT_UNKNOWN; } private static fill(index : number) : String | null { let s : String | null = ""; for (let i : number = 0; i < index; i++){ s = s.valueOf() + " "; } return s; } /** * Setzt die Variable {@code varP} auf TRUE und aktualisiert die gesamte Datenstruktur entsprechend. * * @param varP Die Variable, die auf TRUE gesetzt wird. */ private unitpropagation(varP : Variable) : void { let varN : Variable | null = varP.negation; if (varN === null) throw new NullPointerException() this.heap.remove(varP); this.heap.remove(varN); varP.index = -1; varN.index = -2; this.unitpropagationHelper(varP.clauses, +1, -1); this.unitpropagationHelper(varN.clauses, +0, -1); } /** * Setzt die Variable {@code varP} von TRUE auf FREI und aktualisiert die gesamte Datenstruktur entsprechend. * * @param varP Die Variable, die von TRUE auf FREI gesetzt wird. */ private unitpropagation_undo(varP : Variable) : void { let varN : Variable | null = varP.negation; if (varN === null) throw new NullPointerException() this.unitpropagationHelper(varP.clauses, -1, +1); this.unitpropagationHelper(varN.clauses, +0, +1); this.heap.insert(varP); this.heap.insert(varN); } /** * Eine Hilsmethode, die alle Klauseln der Liste aktualisiert. * * @param clauses Alle zu informmierenden Klausel. * @param pDeltaSat Die Veränderung der erfüllten Klauseln, relativ zum 1. Index im 2D-Array * {@link Variable#statSatFree}. * @param pDeltaFree Die Veränderung der freien Variablen, relativ zum 2. Index im 2D-Array * {@link Variable#statSatFree}. */ private unitpropagationHelper(clauses : LinkedCollection, pDeltaSat : number, pDeltaFree : number) : void { for (let c of clauses) { let sat1 : number = c.sat; let free1 : number = c.free; let sat2 : number = sat1 + pDeltaSat; let free2 : number = free1 + pDeltaFree; for (let v of c.variables) { v.statSatFree[sat1][free1]--; v.statSatFree[sat2][free2]++; this.heap.update(v); } c.sat = sat2; c.free = free2; } } /** * Liefert das zugehörige Variablenobjekt. * * @param pNr Der Variablennummer. * * @return Das zugehörige Variablenobjekt. */ private getVarOf(pNr : number) : Variable { return (pNr > 0) ? this.vArrayPos[pNr] : this.vArrayNeg[-pNr]; } isTranspiledInstanceOf(name : string): boolean { return ['de.nrw.schule.svws.core.kursblockung.satsolver.SatSolverA', 'de.nrw.schule.svws.core.kursblockung.satsolver.SatSolver3'].includes(name); } } export function cast_de_nrw_schule_svws_core_kursblockung_satsolver_SatSolver3(obj : unknown) : SatSolver3 { return obj as SatSolver3; }