├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.sbt ├── diffSAT_2.13-0.5.3.pom ├── doc ├── aspIOutils │ ├── index.html │ └── package$$DisjRule.html ├── diff │ ├── DifferentialFunctionFactoryStasp.html │ └── index.html ├── index.html ├── index.js ├── input │ ├── AspifOrDIMACSPlainParserResult.html │ ├── AspifPlainParser$$AspifRule.html │ ├── AspifPlainParser$.html │ ├── BooleanClause.html │ ├── BooleanFormulaWithCosts.html │ ├── BooleanLiteral.html │ ├── Clause.html │ ├── DIMACPlainSparser$.html │ ├── GroundSymbolicASPRule.html │ ├── InputData.html │ ├── ParseOptimizationTerms$.html │ ├── ProbabilisticAnswerSetProgram.html │ ├── ProbabilisticBooleanClause.html │ ├── SolverParametersOverlay.html │ ├── SymbolicASPRule.html │ ├── diffSAT$$MessageTypes$.html │ ├── diffSAT$.html │ └── index.html ├── lib │ ├── MaterialIcons-Regular.eot │ ├── MaterialIcons-Regular.ttf │ ├── MaterialIcons-Regular.woff │ ├── abstract_type.svg │ ├── annotation.svg │ ├── annotation_comp.svg │ ├── class.svg │ ├── class_comp.svg │ ├── class_diagram.png │ ├── diagrams.css │ ├── index.css │ ├── index.js │ ├── jquery.min.js │ ├── lato-v11-latin-100.eot │ ├── lato-v11-latin-100.ttf │ ├── lato-v11-latin-100.woff │ ├── lato-v11-latin-regular.eot │ ├── lato-v11-latin-regular.ttf │ ├── lato-v11-latin-regular.woff │ ├── object.svg │ ├── object_comp.svg │ ├── object_comp_annotation.svg │ ├── object_comp_trait.svg │ ├── object_diagram.png │ ├── open-sans-v13-latin-400i.eot │ ├── open-sans-v13-latin-400i.ttf │ ├── open-sans-v13-latin-400i.woff │ ├── open-sans-v13-latin-700.eot │ ├── open-sans-v13-latin-700.ttf │ ├── open-sans-v13-latin-700.woff │ ├── open-sans-v13-latin-700i.eot │ ├── open-sans-v13-latin-700i.ttf │ ├── open-sans-v13-latin-700i.woff │ ├── open-sans-v13-latin-regular.eot │ ├── open-sans-v13-latin-regular.ttf │ ├── open-sans-v13-latin-regular.woff │ ├── ownderbg2.gif │ ├── ownerbg.gif │ ├── ownerbg2.gif │ ├── package.svg │ ├── print.css │ ├── ref-index.css │ ├── scheduler.js │ ├── source-code-pro-v6-latin-700.eot │ ├── source-code-pro-v6-latin-700.ttf │ ├── source-code-pro-v6-latin-700.woff │ ├── source-code-pro-v6-latin-regular.eot │ ├── source-code-pro-v6-latin-regular.ttf │ ├── source-code-pro-v6-latin-regular.woff │ ├── template.css │ ├── template.js │ ├── trait.svg │ ├── trait_comp.svg │ ├── trait_diagram.png │ └── type_diagram.png ├── sbt │ └── index.html ├── sharedDefs │ ├── index.html │ ├── package$$AlternativesForThreads.html │ └── package$$Rule.html ├── solving │ ├── NogoodReduciblesSequence.html │ ├── NogoodReduciblesSequenceUnsafe.html │ ├── Preparation$AbsEliClusterable.html │ ├── Preparation$NogoodClusterable.html │ ├── Preparation.html │ ├── SamplingResult.html │ ├── SharedAmongSingleSolverThreads.html │ ├── SingleSolverThreadData$AbsElisPriorityQueue.html │ ├── SingleSolverThreadData.html │ ├── SolverMulti$SampleMultiModelsConf.html │ ├── SolverMulti.html │ ├── SolverThreadSpecificSettings.html │ ├── StochasticLocalSearch$.html │ └── index.html ├── userAPItests │ ├── APITests.html │ └── index.html └── utils │ ├── ArrayValExtensibleIntUnsafe.html │ ├── ArrayValExtensibleLongUnsafe.html │ ├── ByteArrayUnsafeS$.html │ ├── ByteArrayUnsafeS.html │ ├── DoubleArrayUnsafeS$.html │ ├── DoubleArrayUnsafeS.html │ ├── DualIndexedIntSet.html │ ├── FiniteQueue.html │ ├── FloatArrayUnsafeS$.html │ ├── FloatArrayUnsafeS.html │ ├── IntArrayLeanUS$.html │ ├── IntArrayLeanUS.html │ ├── IntArrayUnsafeS$.html │ ├── IntArrayUnsafeS.html │ ├── IntOrLongArrayUnsafe.html │ ├── LongArrayUnsafeS.html │ ├── RandomLongSet.html │ ├── Stats.html │ ├── StatsEntry.html │ ├── Tarjan$.html │ ├── Various$.html │ └── index.html ├── project └── assembly.sbt └── src └── main ├── resources └── META-INF │ ├── MANIFEST.MF │ └── native-image │ └── org.scala-lang │ └── scala-lang │ └── native-image.properties └── scala ├── META-INF └── MANIFEST.MF ├── RuntimeReflectionRegistrationFeature.java ├── diff ├── DifferentialFunctionFactoryStasp.scala └── UncertainAtomsSeprt.java ├── input ├── AspifOrDIMACSPlainParserResult.scala ├── AspifPlainParser.scala ├── BooleanFormulaWithCosts.scala ├── DIMACSPlainParser.scala ├── ParseOptimizationTerms.scala ├── ProbabilisticAnswerSetProgram.scala ├── SolverParametersOverlay.scala ├── Target_scala_runtime_Statics.java ├── UNSAFEhelper.java ├── diffSAT.scala └── package.scala ├── sharedDefs.scala ├── solving ├── NogoodReduciblesSequence.scala ├── NogoodReduciblesSequenceUnsafe.scala ├── Preparation.scala ├── SamplingResult.scala ├── SharedAmongSingleSolverThreads.scala ├── SingleSolverThreadData.scala ├── SolverMulti.scala ├── SolverThreadSpecificSettings.scala └── StochasticLocalSearch.scala ├── userAPItests └── APITests.scala └── utils ├── ASPIOutils.scala ├── ArrayValExtensibleIntUnsafe.scala ├── ArrayValExtensibleLongUnsafe.scala ├── ByteArrayUnsafeS.scala ├── DoubleArrayUnsafeS.scala ├── DualIndexedIntSet.scala ├── FiniteQueue.scala ├── FloatArrayUnsafeS.scala ├── IntArrayLeanUS.scala ├── IntArrayUnsafeS.scala ├── IntOrLongArrayUnsafe.scala ├── LongArrayUnsafeS.scala ├── RandomLongSet.scala ├── SatalyzerUse.scala ├── Tarjan.scala ├── Various.scala ├── XORShift32.java └── XoRoRNGd.java /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Ubuntu] 28 | - Version [e.g. 21.04] 29 | - CPU [e.g. AMD Ryzen 5600X] 30 | - RAM [e.g. 8 GB] 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 0.5.3 2 | 3 | - Support for UTF-8-BOM and UTF-16 character encodings 4 | - Minor bug fixes and improvements 5 | 6 | 0.5.2 7 | 8 | - Minor bug fixes and improvements. delSAT -> diff-SAT 9 | 10 | 0.5.1 11 | 12 | - Bug fix: in rare cases, conflict nogood generation messed up the memory layout 13 | 14 | 0.5.0 15 | 16 | - Optimized SAT solver core (and thus improved sampling performance) 17 | - User API for solving problems with probabilistic or non-probabilistic CNF clauses and ASP ground rules 18 | - Direct User API support for probabilistic non-ground normal rules 19 | - aspif and User API support for weight and choice rules, double default negation in rule bodies and for double and single negation in rule heads 20 | - Probabilistic ASP Intermediate Format (PASPIF) (ASPIF plus a new probabilistic rule type) 21 | - CDNL interleaved with Stochastic Local Search (WalkSAT or Simulated Annealing (SASAT-style)) for regular SAT solving 22 | - Bug fixes and several minor improvements 23 | - Some code cleanup and refactoring 24 | 25 | 0.4.1 26 | 27 | - Support for weighted clauses annotated with probabilities in DIMACS CNF (Probabilistic CNF). See [Usage](README.md#usage) for details. 28 | - `_eval_("term", "?")` meta-predicate for ad hoc querying of probabilities and other statistics (e.g., remaining loss) over sampled models. 29 | An example can be found under [Performing ad hoc queries](README.md#Performing-ad-hoc-queries). 30 | - Argument `-s` k (k>1) for sampling k models _uniformly_ from the multimodel cost solution. 31 | - Argument `-n` -k (k>1) for sampling k models such that k-m of these models are sampled _uniformly_ from the sample (with size m) for which the cost threshold was initially reached. 32 | - Improved finite differences approach to the case where not all parameter atoms are measured (i.e., occur in costs), automatically activated when needed. Switch `useNumFiniteDiff` is 33 | not required anymore, however, purely numerical differentiation approach can still be enforced with `--solverarg mixedScenario false` (in rare cases this is more efficient). 34 | - Typically higher solution entropy for the same sample size (`-n`), compared to previous versions. For how to increase the entropy further see [README.md](README.md). 35 | - Support for non-quoted `_cost_` and `_eval_` terms using plain logic programming term notation (e.g., `_cost_(pow(subtr(div(77,100),f(coin(1,heads))),2))` instead of `_cost_("(0.77-f(coin(1,heads)))^2")`). 36 | - Several minor improvements and bug fixes 37 | - Enhanced documentation (this file) 38 | 39 | 0.4.0 40 | 41 | - Simplified usage; support for parameter atom and cost function specification using special logical predicates 42 | - Support for distinct sets of parameter atoms and measured atoms, enabling preliminary support for inductive/abductive inference. 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Matthias Nickles 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018-2022 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * Licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | name := "diffSAT" 13 | 14 | version := "0.5.3" 15 | 16 | scalaVersion := "2.13.3" // (would probably still work with 2.12 with minor modifications) 17 | 18 | //scalacOptions ++= Seq("-opt:l:method", "-opt:l:inline", 19 | // "-opt-inline-from:solving.SingleSolverThreadData.**;sharedDefs.**;solving.Preparation.**;utils.**") 20 | 21 | scalacOptions ++= Seq("-Xdisable-assertions") // comment out this statement for development and debugging 22 | 23 | mainClass in (Compile, run) := Some("userAPItests.diffSAT") 24 | 25 | mainClass in (Compile, packageBin) := Some("userAPItests.diffSAT") 26 | 27 | //unmanagedJars in Compile += file("C:\\Users\\Me\\Projects\\VMMPredictor\\vmms\\vmm\\out\\artifacts\\VMMPredictor1_jar\\VMMPredictor1.jar") // experimental, currently not used 28 | 29 | // NB: If the "fat jar" isn't built with sbt-assembly, remember to also update artifact modules/versions in IntelliJ 30 | // with all of the following libs by letting "Artifacts" unpack(!) them into the .jar: 31 | 32 | libraryDependencies += "org.scala-lang.modules" %% "scala-parallel-collections" % "0.2.0" 33 | 34 | libraryDependencies += "com.accelad" % "nilgiri-math" % "1.16" 35 | 36 | libraryDependencies += "org.scijava" % "parsington" % "1.0.4" 37 | 38 | libraryDependencies += "it.unimi.dsi" % "fastutil" % "8.3.1" 39 | 40 | libraryDependencies += "org.apache.commons" % "commons-math3" % "3.6.1" 41 | 42 | // The following dependencies are not required for core functionality: 43 | 44 | libraryDependencies += "com.oracle.substratevm" % "svm" % "19.2.1" % Provided // only required if you want 45 | // to create a native image using GraalVM. Also see RuntimeReflectionRegistrationFeature.java and Target_scala_runtime_Statics.java 46 | 47 | libraryDependencies += "com.jsoniter" % "jsoniter" % "0.9.23" // required for stats/persistent logging only 48 | 49 | /* 50 | resolvers += "cibo artifacts" at "https://dl.bintray.com/cibotech/public/" 51 | libraryDependencies += "com.cibo" %% "evilplot" % "0.8.0" // required for stats/persistent logging only 52 | */ 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /diffSAT_2.13-0.5.3.pom: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | default 5 | diffsat_2.13 6 | jar 7 | diffSAT 8 | 0.5.2 9 | diffSAT 10 | 11 | default 12 | 13 | 14 | 15 | org.scala-lang 16 | scala-library 17 | 2.13.3 18 | 19 | 20 | org.scala-lang.modules 21 | scala-parallel-collections_2.13 22 | 0.2.0 23 | 24 | 25 | com.accelad 26 | nilgiri-math 27 | 1.16 28 | 29 | 30 | org.scijava 31 | parsington 32 | 1.0.4 33 | 34 | 35 | it.unimi.dsi 36 | fastutil 37 | 8.3.1 38 | 39 | 40 | org.apache.commons 41 | commons-math3 42 | 3.6.1 43 | 44 | 45 | com.oracle.substratevm 46 | svm 47 | 19.2.1 48 | provided 49 | 50 | 51 | com.jsoniter 52 | jsoniter 53 | 0.9.23 54 | 55 | 56 | -------------------------------------------------------------------------------- /doc/diff/index.html: -------------------------------------------------------------------------------- 1 | delSAT 0.5.2 API - diff

Packages

p

diff

package diff

Type Members

  1. class DifferentialFunctionFactoryStasp extends DifferentialFunctionFactory[DoubleReal]

Ungrouped

3 | -------------------------------------------------------------------------------- /doc/lib/MaterialIcons-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/MaterialIcons-Regular.eot -------------------------------------------------------------------------------- /doc/lib/MaterialIcons-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/MaterialIcons-Regular.ttf -------------------------------------------------------------------------------- /doc/lib/MaterialIcons-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/MaterialIcons-Regular.woff -------------------------------------------------------------------------------- /doc/lib/abstract_type.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | a 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /doc/lib/annotation.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | @ 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /doc/lib/annotation_comp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | @ 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /doc/lib/class.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | C 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /doc/lib/class_comp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | C 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /doc/lib/class_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/class_diagram.png -------------------------------------------------------------------------------- /doc/lib/diagrams.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Material Icons'; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: url(MaterialIcons-Regular.eot); 6 | src: local('Material Icons'), 7 | local('MaterialIcons-Regular'), 8 | url(MaterialIcons-Regular.woff) format('woff'), 9 | url(MaterialIcons-Regular.ttf) format('truetype'); 10 | } 11 | 12 | .material-icons { 13 | font-family: 'Material Icons'; 14 | font-weight: normal; 15 | font-style: normal; 16 | font-size: 24px; 17 | display: inline-block; 18 | width: 1em; 19 | height: 1em; 20 | line-height: 1; 21 | text-transform: none; 22 | letter-spacing: normal; 23 | word-wrap: normal; 24 | white-space: nowrap; 25 | direction: ltr; 26 | -webkit-font-smoothing: antialiased; 27 | text-rendering: optimizeLegibility; 28 | -moz-osx-font-smoothing: grayscale; 29 | font-feature-settings: 'liga'; 30 | } 31 | 32 | .diagram-container { 33 | display: block; 34 | } 35 | 36 | .diagram-container > span.toggle { 37 | z-index: 9; 38 | } 39 | 40 | .diagram { 41 | overflow: hidden; 42 | display: none; 43 | padding-top:15px; 44 | } 45 | 46 | .diagram svg { 47 | display: block; 48 | position: static; 49 | visibility: visible; 50 | z-index: auto; 51 | margin: auto; 52 | } 53 | 54 | .diagram-help { 55 | float:right; 56 | display:none; 57 | } 58 | 59 | .magnifying { 60 | cursor: -webkit-zoom-in ! important; 61 | cursor: -moz-zoom-in ! important; 62 | cursor: pointer; 63 | } 64 | 65 | #close-link { 66 | position: absolute; 67 | z-index: 100; 68 | font-family: Arial, sans-serif; 69 | font-size: 10pt; 70 | text-decoration: underline; 71 | color: #315479; 72 | } 73 | 74 | #close:hover { 75 | text-decoration: none; 76 | } 77 | 78 | #inheritance-diagram { 79 | padding-bottom: 20px; 80 | } 81 | 82 | 83 | #inheritance-diagram-container > span.toggle { 84 | z-index: 2; 85 | } 86 | 87 | .diagram-container.full-screen { 88 | position: fixed !important; 89 | margin: 0; 90 | border-radius: 0; 91 | top: 0em; 92 | bottom: 3em; 93 | left: 0; 94 | width: 100%; 95 | height: 100%; 96 | z-index: 10000; 97 | } 98 | 99 | .diagram-container.full-screen > span.toggle { 100 | display: none; 101 | } 102 | 103 | .diagram-container.full-screen > div.diagram { 104 | position: absolute; 105 | top: 0; right: 0; bottom: 0; left: 0; 106 | margin: auto; 107 | } 108 | 109 | #diagram-controls { 110 | z-index: 2; 111 | position: absolute; 112 | bottom: 1em; 113 | right: 1em; 114 | } 115 | 116 | #diagram-controls > button.diagram-btn { 117 | border-radius: 1.25em; 118 | height: 2.5em; 119 | width: 2.5em; 120 | background-color: #c2c2c2; 121 | color: #fff; 122 | border: 0; 123 | float: left; 124 | margin: 0 0.1em; 125 | cursor: pointer; 126 | line-height: 0.9; 127 | outline: none; 128 | } 129 | 130 | #diagram-controls > button.diagram-btn:hover { 131 | background-color: #e2e2e2; 132 | } 133 | 134 | #diagram-controls > button.diagram-btn > i.material-icons { 135 | font-size: 1.5em; 136 | } 137 | 138 | svg a { 139 | cursor:pointer; 140 | } 141 | 142 | svg text { 143 | font-size: 8.5px; 144 | } 145 | 146 | 147 | svg { 148 | border: 1px solid #999; 149 | overflow: hidden; 150 | } 151 | 152 | svg .node { 153 | white-space: nowrap; 154 | } 155 | 156 | svg .node rect, 157 | svg .node circle, 158 | svg .node ellipse { 159 | stroke: #333; 160 | fill: #fff; 161 | stroke-width: 1.5px; 162 | } 163 | 164 | svg .cluster rect { 165 | stroke: #333; 166 | fill: #000; 167 | fill-opacity: 0.1; 168 | stroke-width: 1.5px; 169 | } 170 | 171 | svg .edgePath path.path { 172 | stroke: #333; 173 | stroke-width: 1.5px; 174 | fill: none; 175 | } 176 | 177 | 178 | -------------------------------------------------------------------------------- /doc/lib/lato-v11-latin-100.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/lato-v11-latin-100.eot -------------------------------------------------------------------------------- /doc/lib/lato-v11-latin-100.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/lato-v11-latin-100.ttf -------------------------------------------------------------------------------- /doc/lib/lato-v11-latin-100.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/lato-v11-latin-100.woff -------------------------------------------------------------------------------- /doc/lib/lato-v11-latin-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/lato-v11-latin-regular.eot -------------------------------------------------------------------------------- /doc/lib/lato-v11-latin-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/lato-v11-latin-regular.ttf -------------------------------------------------------------------------------- /doc/lib/lato-v11-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/lato-v11-latin-regular.woff -------------------------------------------------------------------------------- /doc/lib/object.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | O 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /doc/lib/object_comp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | O 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /doc/lib/object_comp_annotation.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | O 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /doc/lib/object_comp_trait.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | O 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /doc/lib/object_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/object_diagram.png -------------------------------------------------------------------------------- /doc/lib/open-sans-v13-latin-400i.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/open-sans-v13-latin-400i.eot -------------------------------------------------------------------------------- /doc/lib/open-sans-v13-latin-400i.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/open-sans-v13-latin-400i.ttf -------------------------------------------------------------------------------- /doc/lib/open-sans-v13-latin-400i.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/open-sans-v13-latin-400i.woff -------------------------------------------------------------------------------- /doc/lib/open-sans-v13-latin-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/open-sans-v13-latin-700.eot -------------------------------------------------------------------------------- /doc/lib/open-sans-v13-latin-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/open-sans-v13-latin-700.ttf -------------------------------------------------------------------------------- /doc/lib/open-sans-v13-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/open-sans-v13-latin-700.woff -------------------------------------------------------------------------------- /doc/lib/open-sans-v13-latin-700i.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/open-sans-v13-latin-700i.eot -------------------------------------------------------------------------------- /doc/lib/open-sans-v13-latin-700i.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/open-sans-v13-latin-700i.ttf -------------------------------------------------------------------------------- /doc/lib/open-sans-v13-latin-700i.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/open-sans-v13-latin-700i.woff -------------------------------------------------------------------------------- /doc/lib/open-sans-v13-latin-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/open-sans-v13-latin-regular.eot -------------------------------------------------------------------------------- /doc/lib/open-sans-v13-latin-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/open-sans-v13-latin-regular.ttf -------------------------------------------------------------------------------- /doc/lib/open-sans-v13-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/open-sans-v13-latin-regular.woff -------------------------------------------------------------------------------- /doc/lib/ownderbg2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/ownderbg2.gif -------------------------------------------------------------------------------- /doc/lib/ownerbg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/ownerbg.gif -------------------------------------------------------------------------------- /doc/lib/ownerbg2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/ownerbg2.gif -------------------------------------------------------------------------------- /doc/lib/package.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | p 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /doc/lib/print.css: -------------------------------------------------------------------------------- 1 | @media print { 2 | * { 3 | text-decoration: none; 4 | font-family: "Lato", Arial, sans-serif; 5 | border-width: 0px; 6 | margin: 0px; 7 | } 8 | #textfilter, #package, #subpackage-spacer, #memberfilter, #filterby, div#definition .big-circle { 9 | display: none !important; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /doc/lib/ref-index.css: -------------------------------------------------------------------------------- 1 | /* fonts */ 2 | @font-face { 3 | font-family: 'Source Code Pro'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: url('source-code-pro-v6-latin-regular.eot'); 7 | src: local('Source Code Pro'), local('SourceCodePro-Regular'), 8 | url('source-code-pro-v6-latin-regular.eot?#iefix') format('embedded-opentype'), 9 | url('source-code-pro-v6-latin-regular.woff') format('woff'), 10 | url('source-code-pro-v6-latin-regular.ttf') format('truetype'); 11 | } 12 | @font-face { 13 | font-family: 'Source Code Pro'; 14 | font-style: normal; 15 | font-weight: 700; 16 | src: url('source-code-pro-v6-latin-700.eot'); 17 | src: local('Source Code Pro Bold'), local('SourceCodePro-Bold'), 18 | url('source-code-pro-v6-latin-700.eot?#iefix') format('embedded-opentype'), 19 | url('source-code-pro-v6-latin-700.woff') format('woff'), 20 | url('source-code-pro-v6-latin-700.ttf') format('truetype'); 21 | } 22 | 23 | body { 24 | font-size: 10pt; 25 | font-family: Arial, sans-serif; 26 | } 27 | 28 | a { 29 | color:#315479; 30 | } 31 | 32 | .letters { 33 | width:100%; 34 | text-align:center; 35 | margin:0.6em; 36 | padding:0.1em; 37 | border-bottom:1px solid gray; 38 | } 39 | 40 | div.entry { 41 | padding: 0.5em; 42 | background-color: #e1e7ed; 43 | border-radius: 0.2em; 44 | color: #103a51; 45 | margin: 0.5em 0; 46 | } 47 | 48 | .name { 49 | font-family: "Source Code Pro"; 50 | font-size: 1.1em; 51 | } 52 | 53 | .occurrences { 54 | margin-left: 1em; 55 | margin-top: 5px; 56 | } 57 | -------------------------------------------------------------------------------- /doc/lib/scheduler.js: -------------------------------------------------------------------------------- 1 | // © 2010 EPFL/LAMP 2 | // code by Gilles Dubochet, Felix Mulder 3 | 4 | function Scheduler() { 5 | var scheduler = this; 6 | var resolution = 0; 7 | this.timeout = undefined; 8 | this.queues = new Array(0); // an array of work packages indexed by index in the labels table. 9 | this.labels = new Array(0); // an indexed array of labels indexed by priority. This should be short. 10 | 11 | this.label = function(name, priority) { 12 | this.name = name; 13 | this.priority = priority; 14 | } 15 | 16 | this.work = function(fn, self, args) { 17 | this.fn = fn; 18 | this.self = self; 19 | this.args = args; 20 | } 21 | 22 | this.addLabel = function(name, priority) { 23 | var idx = 0; 24 | while (idx < scheduler.queues.length && scheduler.labels[idx].priority <= priority) { idx = idx + 1; } 25 | scheduler.labels.splice(idx, 0, new scheduler.label(name, priority)); 26 | scheduler.queues.splice(idx, 0, new Array(0)); 27 | } 28 | 29 | this.clearLabel = function(name) { 30 | var idx = scheduler.indexOf(name); 31 | if (idx != -1) { 32 | scheduler.labels.splice(idx, 1); 33 | scheduler.queues.splice(idx, 1); 34 | } 35 | } 36 | 37 | this.nextWork = function() { 38 | var fn = undefined; 39 | var idx = 0; 40 | while (idx < scheduler.queues.length && scheduler.queues[idx].length == 0) { idx = idx + 1; } 41 | 42 | if (idx < scheduler.queues.length && scheduler.queues[idx].length > 0) 43 | var fn = scheduler.queues[idx].shift(); 44 | 45 | return fn; 46 | } 47 | 48 | this.add = function(labelName, fn, self, args) { 49 | var doWork = function() { 50 | scheduler.timeout = setTimeout(function() { 51 | var work = scheduler.nextWork(); 52 | if (work != undefined) { 53 | if (work.args == undefined) { work.args = new Array(0); } 54 | work.fn.apply(work.self, work.args); 55 | doWork(); 56 | } 57 | else { 58 | scheduler.timeout = undefined; 59 | } 60 | }, resolution); 61 | } 62 | 63 | var idx = scheduler.indexOf(labelName) 64 | if (idx != -1) { 65 | scheduler.queues[idx].push(new scheduler.work(fn, self, args)); 66 | if (scheduler.timeout == undefined) doWork(); 67 | } else { 68 | throw("queue for add is non-existent"); 69 | } 70 | } 71 | 72 | this.clear = function(labelName) { 73 | scheduler.queues[scheduler.indexOf(labelName)] = new Array(); 74 | } 75 | 76 | this.indexOf = function(label) { 77 | var idx = 0; 78 | while (idx < scheduler.labels.length && scheduler.labels[idx].name != label) 79 | idx++; 80 | 81 | return idx < scheduler.queues.length && scheduler.labels[idx].name == label ? idx : -1; 82 | } 83 | 84 | this.queueEmpty = function(label) { 85 | var idx = scheduler.indexOf(label); 86 | if (idx != -1) 87 | return scheduler.queues[idx].length == 0; 88 | else 89 | throw("queue for label '" + label + "' is non-existent"); 90 | } 91 | 92 | this.scheduleLast = function(label, fn) { 93 | if (scheduler.queueEmpty(label)) { 94 | fn(); 95 | } else { 96 | scheduler.add(label, function() { 97 | scheduler.scheduleLast(label, fn); 98 | }); 99 | } 100 | } 101 | 102 | this.numberOfJobs = function(label) { 103 | var index = scheduler.indexOf(label); 104 | if (index == -1) throw("queue for label '" + label + "' non-existent"); 105 | 106 | return scheduler.queues[index].length; 107 | } 108 | }; 109 | -------------------------------------------------------------------------------- /doc/lib/source-code-pro-v6-latin-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/source-code-pro-v6-latin-700.eot -------------------------------------------------------------------------------- /doc/lib/source-code-pro-v6-latin-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/source-code-pro-v6-latin-700.ttf -------------------------------------------------------------------------------- /doc/lib/source-code-pro-v6-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/source-code-pro-v6-latin-700.woff -------------------------------------------------------------------------------- /doc/lib/source-code-pro-v6-latin-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/source-code-pro-v6-latin-regular.eot -------------------------------------------------------------------------------- /doc/lib/source-code-pro-v6-latin-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/source-code-pro-v6-latin-regular.ttf -------------------------------------------------------------------------------- /doc/lib/source-code-pro-v6-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/source-code-pro-v6-latin-regular.woff -------------------------------------------------------------------------------- /doc/lib/trait.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | t 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /doc/lib/trait_comp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | t 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /doc/lib/trait_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/trait_diagram.png -------------------------------------------------------------------------------- /doc/lib/type_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthiasNickles/diff-SAT/1ca0bdff5790b537bb4855bd79620f42121b877b/doc/lib/type_diagram.png -------------------------------------------------------------------------------- /doc/userAPItests/index.html: -------------------------------------------------------------------------------- 1 | delSAT 0.5.2 API - userAPItests

Packages

p

userAPItests

package userAPItests

Type Members

  1. trait APITests extends AnyRef

    User API tests.

    User API tests.

    Work in progress to replace older diff-SAT utests in Prasp2. TODO: add more tests

    Observe that we are using two major approaches: input.ProbabilisticAnswerSetProgram for (probabilistic or plain) answer set programming, 3 | and input.BooleanFormulaWithCosts for (probabilistic or plain) Boolean formulas (in CNF). Each of these can 4 | also be used with arbitrary differentiable cost functions (in fact probabilistic weights are translated into such functions anyway).

    From the commandline, run using --apitests

    For higher level blackbox testing with .cnf, .aspif and .opt files, use batch tests (--batchtests <folder>)

Ungrouped

5 | -------------------------------------------------------------------------------- /project/assembly.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9") -------------------------------------------------------------------------------- /src/main/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: input.diffSAT 3 | 4 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/org.scala-lang/scala-lang/native-image.properties: -------------------------------------------------------------------------------- 1 | Args = --initialize-at-build-time=scala.runtime.Statics$VM 2 | -------------------------------------------------------------------------------- /src/main/scala/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: input.diffSAT 3 | 4 | -------------------------------------------------------------------------------- /src/main/scala/RuntimeReflectionRegistrationFeature.java: -------------------------------------------------------------------------------- 1 | // Activate if native image generation using Graal is intended. 2 | // !!! Also see build.sbt and Target_scala_runtime_Statics.java !!! 3 | 4 | import com.oracle.svm.core.annotate.AutomaticFeature; 5 | import org.graalvm.nativeimage.hosted.Feature; 6 | import org.graalvm.nativeimage.hosted.RuntimeReflection; 7 | 8 | import java.lang.reflect.Field; 9 | 10 | // Tells Graal's native image generator about reflected fields in sharedDefs (otherwise this information 11 | // wouldn't be available anymore at diff-SAT runtime). 12 | // native-image.cmd must be called with argument --features=RuntimeReflectionRegistrationFeature 13 | @AutomaticFeature 14 | public class RuntimeReflectionRegistrationFeature implements Feature { 15 | 16 | public void beforeAnalysis(BeforeAnalysisAccess access) { 17 | 18 | try { 19 | 20 | RuntimeReflection.register(sharedDefs.package$.class); 21 | 22 | Field[] paramFields = sharedDefs.package$.class.getDeclaredFields(); 23 | 24 | for (int i = 0; i < paramFields.length; i++) { 25 | 26 | //System.out.println(">>" + paramFields[i].getName() + "<<"); 27 | 28 | //RuntimeReflection.register(true, 29 | // sharedDefs.package$.class.getDeclaredField("abandonOrSwitchSlowThreads")); 30 | 31 | RuntimeReflection.register(sharedDefs.package$.class.getDeclaredField(paramFields[i].getName())); 32 | // NB: field must be var, not val in sharedDefs 33 | 34 | } 35 | 36 | 37 | } catch (Exception e) { 38 | 39 | System.err.println(e); 40 | 41 | } 42 | 43 | } 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/main/scala/diff/DifferentialFunctionFactoryStasp.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018-2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package diff 13 | 14 | import com.accelad.math.nilgiri.autodiff.DifferentialFunctionFactory 15 | import com.accelad.math.nilgiri.{DoubleReal, DoubleRealFactory} 16 | 17 | class DifferentialFunctionFactoryStasp extends DifferentialFunctionFactory[DoubleReal](DoubleRealFactory.instance()) { } -------------------------------------------------------------------------------- /src/main/scala/diff/UncertainAtomsSeprt.java: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018,2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package diff; 13 | 14 | import com.accelad.math.nilgiri.DoubleReal; 15 | import com.accelad.math.nilgiri.autodiff.DifferentialFunction; 16 | import com.accelad.math.nilgiri.autodiff.Variable; 17 | 18 | import scala.Int; 19 | import scala.collection.Map; 20 | 21 | import java.io.Serializable; 22 | 23 | public class UncertainAtomsSeprt implements Serializable { // for interoperability reasons, this is a Java class 24 | 25 | public String parameterAtomsSeq[]; 26 | 27 | public String measuredAtomsSeq[]; 28 | 29 | public DifferentialFunction innerCostExpressionInstances[]; 30 | 31 | public scala.collection.mutable.HashMap> eliToVariableInCostFunctions; 32 | 33 | public Map> evalExpressionToFct; 34 | 35 | public UncertainAtomsSeprt(String parameterAtomsSeq[], 36 | String measuredAtomsSeq[], 37 | DifferentialFunction innerCostExpressionInstances[], 38 | scala.collection.mutable.HashMap> eliToVariableInCostFunctions, 39 | Map> evalExpressionToFct) { 40 | 41 | this.parameterAtomsSeq = parameterAtomsSeq; 42 | 43 | this.measuredAtomsSeq = measuredAtomsSeq; 44 | 45 | this.innerCostExpressionInstances = innerCostExpressionInstances; 46 | 47 | this.eliToVariableInCostFunctions = eliToVariableInCostFunctions; 48 | 49 | this.evalExpressionToFct = evalExpressionToFct; 50 | 51 | } 52 | 53 | } 54 | 55 | -------------------------------------------------------------------------------- /src/main/scala/input/AspifOrDIMACSPlainParserResult.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018-2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details). 9 | * 10 | */ 11 | 12 | package input 13 | 14 | import aspIOutils.Pred 15 | import diff.UncertainAtomsSeprt 16 | import input.AspifPlainParser.{AspifEli, AspifRule} 17 | import sharedDefs.{Eli, Rule} 18 | import utils.IntArrayUnsafeS 19 | 20 | import scala.collection.mutable 21 | import scala.collection.mutable.ArrayBuffer 22 | 23 | /** Result of the aspif or dimacs parser. Also see [[input.InputData]] */ 24 | final case class AspifOrDIMACSPlainParserResult(symbols: Array[String], 25 | rulesOrClauseNogoods: Either[ 26 | /*from aspif:*/ ArrayBuffer[Rule], 27 | /*from DIMACS:*/ Array[IntArrayUnsafeS]], 28 | noOfPosBlits: Int, 29 | noOfDummySymbols: Int, 30 | externalAtomElis: Seq[Eli], // for aspif. TODO: #external not implemented yet 31 | assumptionElis: Seq[Eli], // filtering assumptions 32 | emptyBodyBlit: Int = -1, 33 | clausesForChecksOpt: Option[Array[Array[Int]]], 34 | symbolToEliOpt: Option[Predef.Map[String, Eli]], 35 | additionalUncertainAtomsInnerCostsStrs: (Array[String], Array[String], Array[String] /*<-original _eval_ terms*/ ), 36 | // ^ we allow probabilistic parameter atoms to be 37 | // stated as ASP facts too in the aspif, in addition to those provided using "pats" and "cost" lines. 38 | // They are treated as MSE inner cost functions. 39 | 40 | // The following elements are useful if the parser result should be extended with further rules, which 41 | // needs to happen on the aspif-eli level, not on the eli-level: 42 | aspifEliToSymbolOpt: Option[mutable.HashMap[AspifEli, Pred]] = None, 43 | aspifRulesOpt: Option[mutable.ArrayBuffer[AspifRule]] = None 44 | ) 45 | 46 | /** Input data for the sampler/solver */ 47 | final case class InputData(aspifOrDIMACSPlainParserResult: AspifOrDIMACSPlainParserResult, 48 | costsOpt: Option[UncertainAtomsSeprt]) -------------------------------------------------------------------------------- /src/main/scala/input/BooleanFormulaWithCosts.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018-2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details). 9 | * 10 | */ 11 | 12 | package input 13 | 14 | import aspIOutils.Pred 15 | import diffSAT.invokeSampler 16 | import utils.ArrayValExtensibleIntUnsafe 17 | import solving.SamplingResult 18 | import utils.IntArrayUnsafeS 19 | 20 | import scala.collection.mutable.ArrayBuffer 21 | 22 | 23 | /** 24 | * A collection of probabilistic and/or non-probabilistic Boolean clauses (disjunctions of literals) 25 | * 26 | * User API part for probabilistic SAT solving and multimodel optimization (for the User API part for probabilistic Answer Set 27 | * Programming see [[input.ProbabilisticAnswerSetProgram]]) 28 | * 29 | * Example: 30 | * Construct CNF and specify multimodel optimization goal: 31 | * {{{ 32 | 33 | // The following API calls corresponds to solving the following Enhanced DIMACS CNF file: 34 | // 35 | // 1 -1 0 36 | // 2 -2 0 37 | // -1 -2 0 38 | // 39 | // pats 1 2 40 | // 41 | // cost (0.2-f(v1))^2 42 | // cost (0.5-f(v2))^2 43 | // 44 | 45 | val clause1 = BooleanClause(literals = Set(BooleanLiteral(1), BooleanLiteral(-1))) 46 | 47 | val clause2 = BooleanClause(literals = Set(BooleanLiteral(2), BooleanLiteral(-2))) 48 | 49 | val clause3 = BooleanClause(literals = Set(BooleanLiteral(-1), BooleanLiteral(-2))) 50 | 51 | val booleanFormula: BooleanFormulaWithCosts = BooleanFormulaWithCosts(Set(clause1, clause2, clause3)) 52 | 53 | val probabilisticObjectives = // Parameter atoms are "1" and "2". Atom "1" has probability 0.2, "2" has probability 0.5 54 | """pats 1 2 55 | 56 | cost (0.2-f(v1))^2 57 | cost (0.5-f(v2))^2 58 | """ 59 | // (Alternatively, cost (loss) terms can be constructed programmatically (see ParseOptimizationTerms to get an idea how to do 60 | // this. Also consider using the ASP User API ([[input.ProbabilisticAnswerSetProgram]]) which allows to work 61 | // with symbolic atoms and rules.) 62 | }}} 63 | Invoke sampler and examine the resulting sample (a set of models): 64 | {{{ 65 | 66 | val solverParams = input.SolverParametersOverlay( 67 | noOfModels = -1, // -1 means sample until desired accuracy (thresholdOpt) has been reached. A positive 68 | // number would specify a minimum sample size (number of answer sets). 69 | noOfSecondaryModels = 0, 70 | offHeapGarbageCollectionModeR = 0, 71 | thresholdOpt = Some(0.001d), // the desired accuracy (lower = more accurate but sampling requires more time) 72 | assureMSE = true, // true = the loss function is assured to be of type MSE (false works too but true is more efficient) 73 | showauxInSATmode = false, 74 | advancedSolverArgs = mutable.HashMap[(String, Int), String]( // advanced solver parameters (corresponding to --solverarg commandline parameters) 75 | ("seedRngGlobalR", 0) -> "-1" // uses a random PRNG seed seed for each run. Otherwise, we would get the same set of models at each call. 76 | , ("diversify", 0) -> "false" // "true" increases the entropy - with false, we might get a highly non-uniform distribution if there are no probabilities specified 77 | ) 78 | ) 79 | 80 | val sampled: SamplingResult = booleanFormula.solve(solverParams, 81 | paramAtomsAndInnerCostsStrOpt = Some(probabilisticObjectives)) 82 | 83 | // Print sample and the result of ad hoc query Pr[p(a):-not q AND p(b):-not q]: 84 | 85 | val (_, adHocConjunctiveQueriesResults, adHocDisjunctiveQueriesResults, adHocRuleQueriesResults, 86 | adHocConjunctionOfSimpleGroundRulesQuery) = diffSAT.queryAndPrintSolverResult(showauxInASPmode = false, 87 | satMode = true, 88 | samplingResult = sampled, 89 | adHocConjunctiveQueries = Seq(Seq("1"), Seq("2")), 90 | adHocDisjunctiveQueries = Seq(), 91 | adHocConjunctionOfSimpleGroundRulesQuery = Seq(), 92 | printAdHocQueryResults = true, 93 | printAnswers = true) 94 | 95 | println(adHocConjunctiveQueriesResults) 96 | 97 | }}} 98 | * 99 | * Further examples can be found in the source code of [[userAPItests.APITests]] 100 | * 101 | * @param clauses 102 | * @param maxVar : largest (by name) propositional variable. If -1 it will be automatically determined. 103 | */ 104 | case class BooleanFormulaWithCosts(clauses: Set[Clause], var maxVar: Int = -1) { 105 | 106 | val patsFromProbabilisticClauses = ArrayBuffer[Pred]() 107 | 108 | val costsFromProbabilisticClauses = ArrayBuffer[String]() // format of an inner cost term obtained from a probabilistic 109 | // clause is "(probability-f(vx))^2" 110 | 111 | // We need to know maxVar upfront 1) to fill "gaps" in the enumeration of variables (symbols) and 2) for new variables introduced for extra clauses from probabilistic clauses 112 | 113 | if (maxVar < 0) { 114 | 115 | clauses.foreach { 116 | 117 | case BooleanClause(literals: Set[BooleanLiteral]) => 118 | literals.foreach(lit => maxVar = maxVar.max(Math.abs(lit.value))) 119 | 120 | case ProbabilisticBooleanClause(literals: Set[BooleanLiteral], _: Double) => 121 | literals.foreach(lit => maxVar = maxVar.max(Math.abs(lit.value))) 122 | 123 | } 124 | 125 | } 126 | 127 | val originalMaxVar = maxVar 128 | 129 | val directClauseNogoods = clauses.flatMap { 130 | 131 | case BooleanClause(literals: Set[BooleanLiteral]) => { 132 | 133 | val arrayUS = new IntArrayUnsafeS(values = literals.map(lit => { 134 | 135 | -lit.value // (-value since diff-SAT internally works with nogoods, not clauses) 136 | 137 | }).toArray) 138 | 139 | Set(arrayUS) 140 | 141 | } 142 | 143 | case ProbabilisticBooleanClause(literals: Set[BooleanLiteral], probability: Double) => { 144 | 145 | maxVar += 1 146 | 147 | val llc = new ArrayValExtensibleIntUnsafe(buffer = new IntArrayUnsafeS(1024)) 148 | 149 | llc.append(0) // placeholder required in createExtraClausesNogoodsFromProbabilisticClauseNogood() 150 | 151 | literals.foreach(literal => llc.append(-literal.value)) 152 | 153 | patsFromProbabilisticClauses.append(maxVar.toString) // the parameter variables (which in this case are also measured variables) 154 | 155 | val (extraClauseA: ArrayValExtensibleIntUnsafe, extraClausesB: Array[ArrayValExtensibleIntUnsafe], 156 | costTermForProbClause: String) = 157 | DIMACPlainSparser.createExtraClausesNogoodsFromProbabilisticClauseNogood(llc, 158 | clauseHandleVar = maxVar, 159 | weightStr = probability.toString) 160 | 161 | costsFromProbabilisticClauses.append(costTermForProbClause) 162 | 163 | Set(extraClauseA.getContentUnsafe) ++ extraClausesB.map(_.getContentUnsafe) 164 | 165 | } 166 | 167 | }.toArray 168 | 169 | println("Auxiliary Boolean variables introduced for PCNF: " + (originalMaxVar to maxVar).mkString(",")) 170 | 171 | /** 172 | * Calls the sampler for this propositional formula. Optionally with additional multimodel cost terms (in addition 173 | * to those generated from any probabilistic clauses) 174 | * 175 | * @param solverParametersOverlay 176 | * @param paramAtomsAndInnerCostsStrOpt An optional string with a specification of parameter atoms and cost terms 177 | * (see README.md). These costs are in addition to those specified indirectly using 178 | * probabilistic clauses. 179 | * @return SamplingResult The list of models is in SamplingResult.modelsSymbolic. Since sampling 180 | * is with replacement, the list represents a multiset of models. 181 | */ 182 | def solve(solverParametersOverlay: SolverParametersOverlay, 183 | paramAtomsAndInnerCostsStrOpt: Option[String] = None): SamplingResult = { 184 | 185 | val symbolsArray = (1 to maxVar).map(_.toString).toArray 186 | 187 | //println(directClauseNogoods.mkString("\n")) 188 | 189 | val pcnf = AspifOrDIMACSPlainParserResult(symbols = symbolsArray, 190 | rulesOrClauseNogoods = Right(directClauseNogoods), 191 | noOfPosBlits = 0, 192 | noOfDummySymbols = 0, 193 | externalAtomElis = Seq(), 194 | assumptionElis = Seq(), 195 | clausesForChecksOpt = None, 196 | symbolToEliOpt = None, 197 | additionalUncertainAtomsInnerCostsStrs = (patsFromProbabilisticClauses.toArray, 198 | costsFromProbabilisticClauses.toArray, 199 | Array[String]())) 200 | 201 | val (inputData: InputData, _) = ParseOptimizationTerms.parseOptimizationTerms(mse = solverParametersOverlay.assureMSE, 202 | paramAtomsAndInnerCostsStrOpt = paramAtomsAndInnerCostsStrOpt, 203 | satMode = true, 204 | aspifOrDIMACSParserResult = pcnf) 205 | 206 | val (samplingResult: SamplingResult, _: AspifOrDIMACSPlainParserResult) = 207 | invokeSampler(inputData, solverParametersOverlay, baseSettingsForBatchTestsOpt = None, satMode = true) 208 | 209 | samplingResult 210 | 211 | } 212 | 213 | } 214 | 215 | class Clause 216 | 217 | /** 218 | * A disjunctive set of literals (hard clause) 219 | * 220 | * @param literals 221 | */ 222 | case class BooleanClause(literals: Set[BooleanLiteral]) extends Clause {} 223 | 224 | /** 225 | * A disjunctive set of literals, annotated with a probability (soft clause). 226 | * Observe that every probabilistic clause causes diff-SAT to introduce a new auxiliary propositional variable 227 | * 228 | * @param literals 229 | */ 230 | case class ProbabilisticBooleanClause(literals: Set[BooleanLiteral], probability: Double) extends Clause {} 231 | 232 | 233 | /** 234 | * A literal, represented as a positive or negative integer value. The sign represents its logical polarity. 235 | * Value 0 is not allowed. 236 | * 237 | * @param value 238 | */ 239 | case class BooleanLiteral(value: Int) extends AnyVal 240 | 241 | -------------------------------------------------------------------------------- /src/main/scala/input/SolverParametersOverlay.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018,2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details). 9 | * 10 | */ 11 | 12 | package input 13 | 14 | import scala.collection.mutable 15 | 16 | /** 17 | * Solver and sampling settings. For advancedSolverArgs and use, see [[sharedDefs]] 18 | * 19 | * @param noOfModels -1: sample until accuracy threshold (thresholdOpt) has been reached 20 | * @param noOfSecondaryModels sample n models uniformly from the original sample 21 | * @param offHeapGarbageCollectionModeR 22 | * @param thresholdOpt 23 | * @param assureMSE guarantees that the cost functions are in MSE form (see README.md), enables certain performance optimizations 24 | * @param showauxInSATmode 25 | * @param advancedSolverArgs See [[sharedDefs]] 26 | */ 27 | case class SolverParametersOverlay( 28 | noOfModels: Int, 29 | noOfSecondaryModels: Int, 30 | offHeapGarbageCollectionModeR: Int, 31 | thresholdOpt: Option[Double], 32 | assureMSE: Boolean, 33 | showauxInSATmode: Boolean, // TODO: this works in SAT mode only 34 | advancedSolverArgs: mutable.HashMap[(String /*param name*/ , Int /*<-thread*/ ), String /*param value*/ ] = mutable.HashMap() 35 | 36 | ) 37 | -------------------------------------------------------------------------------- /src/main/scala/input/Target_scala_runtime_Statics.java: -------------------------------------------------------------------------------- 1 | package input; 2 | 3 | // Activate if native image generation using Graal is intended. 4 | // !!! Also see build.sbt and RuntimeReflectionRegistrationFeature.java !!! 5 | 6 | // For native diff-SAT image creation using GraalVM 20.0 (see https://github.com/scala/bug/issues/11634) 7 | // Requires libraryDependencies += "com.oracle.substratevm" % "svm" % "19.2.1" % Provided in build.sbt 8 | 9 | import com.oracle.svm.core.annotate.*; 10 | 11 | @TargetClass(className = "scala.runtime.Statics") 12 | final class Target_scala_runtime_Statics { 13 | 14 | @Substitute 15 | public static void releaseFence() { 16 | UNSAFEhelper.UNSAFE.storeFence(); 17 | } 18 | 19 | } 20 | 21 | -------------------------------------------------------------------------------- /src/main/scala/input/UNSAFEhelper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018,2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details). 9 | */ 10 | 11 | package input; 12 | 13 | import com.sun.management.HotSpotDiagnosticMXBean; 14 | import it.unimi.dsi.fastutil.objects.ReferenceArrayList; 15 | 16 | import java.lang.management.ManagementFactory; 17 | import java.lang.reflect.Field; 18 | import java.util.Arrays; 19 | 20 | import java.util.concurrent.ConcurrentHashMap; 21 | import java.util.concurrent.atomic.AtomicLong; 22 | 23 | 24 | 25 | import static org.apache.commons.math3.util.Precision.round; 26 | 27 | public class UNSAFEhelper { 28 | 29 | public static final sun.misc.Unsafe UNSAFE; 30 | 31 | static AtomicLong allocatedOffHeapDiffSATGlobal = new AtomicLong(0l); 32 | 33 | public static final boolean debugMode = false; 34 | 35 | static ConcurrentHashMap allocs = null; // for debugging only - don't use to trace off-heap space regularly! 36 | 37 | public static ReferenceArrayList garbage = new ReferenceArrayList<>(); // (unclear whether stack access 38 | // via push/pop or queue access would be better - probably doesn't make much of a difference. But 39 | // use of ConcurrentLinkedQueue instead ReferenceArrayList is slower (if called with no or little concurrency anyway). 40 | // Important: we DON'T add anything to garbage during SAT solving. Only after all the core solver threads 41 | // have been stopped, we add memory addresses to garbage. 42 | 43 | static { 44 | 45 | if (debugMode) allocs = new ConcurrentHashMap(); 46 | 47 | } 48 | 49 | static { 50 | 51 | try { 52 | 53 | Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); 54 | 55 | field.setAccessible(true); 56 | 57 | UNSAFE = (sun.misc.Unsafe) field.get(null); 58 | 59 | } catch (Throwable ex) { 60 | 61 | throw new ExceptionInInitializerError(ex); 62 | 63 | } 64 | 65 | } 66 | 67 | static long estimatedAvailableForUnsafe = Runtime.getRuntime().maxMemory(); // NB: this is what Java by default also allows for direct buffer allocations but 68 | // it tends to be an underestimate for the off-heap space available. TODO: more accurate estimation wanted (without 69 | // asking the OS or using Native Memory Tracking (NMT)) 70 | 71 | static final HotSpotDiagnosticMXBean vmOptions = ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class); 72 | 73 | static { 74 | 75 | long maxDirectMemorySize = vmOptions == null ? 0l : Long.valueOf(vmOptions.getVMOption("MaxDirectMemorySize").getValue()); 76 | 77 | // Observe that sun.misc.VM.maxDirectMemory() isn't available in recent JVMs. 78 | 79 | if (maxDirectMemorySize > 0l) 80 | estimatedAvailableForUnsafe = maxDirectMemorySize; 81 | 82 | if (debugMode) 83 | System.out.println("Off-heap memory estimate: " + approxSizeOfCurrentFreeUnsafeMemory() + " (" + Double.valueOf(approxSizeOfCurrentFreeUnsafeMemory()) / 1073741824d + " GB)"); 84 | 85 | } 86 | 87 | static String formatCallerInfo(StackTraceElement[] stackStrace) { 88 | 89 | String callTreeStr = Arrays.toString(stackStrace); 90 | 91 | String callerInfo = callTreeStr; 92 | 93 | return callerInfo; 94 | 95 | } 96 | 97 | public static long allocateOffHeapMem(long size) { // calling this method alone does NOT make it garbage collectable! 98 | 99 | allocatedOffHeapDiffSATGlobal.getAndAdd(size); 100 | 101 | if (debugMode) { 102 | 103 | long a = UNSAFE.allocateMemory(size); 104 | 105 | allocs.put(a, formatCallerInfo(new Throwable().fillInStackTrace().getStackTrace())); 106 | 107 | return a; 108 | 109 | } else 110 | return UNSAFE.allocateMemory(size); 111 | 112 | } 113 | 114 | public static long resizeOffHeapMem(long oldAddress, long oldSize, long newSize) { // calling this method alone does NOT make it garbage collectable! 115 | 116 | allocatedOffHeapDiffSATGlobal.getAndAdd(-oldSize + newSize); 117 | 118 | if (debugMode) { 119 | 120 | long a = UNSAFE.reallocateMemory(oldAddress, newSize); 121 | 122 | allocs.remove(oldAddress); 123 | 124 | allocs.put(a, formatCallerInfo(new Throwable().fillInStackTrace().getStackTrace())); 125 | 126 | // System.out.println("FREED (by resize) " + oldAddress + ", bytes: " + oldSize + ", caller: " + callerInfo); 127 | 128 | // System.out.println("ALLOCATED (by resize) " + a + ", bytes: " + newSize + ", caller: " + callerInfo); 129 | 130 | return a; 131 | 132 | } else 133 | return UNSAFE.reallocateMemory(oldAddress, newSize); 134 | 135 | } 136 | 137 | public static void freeOffHeapMem(long address, long size) { 138 | 139 | allocatedOffHeapDiffSATGlobal.getAndAdd(-size); 140 | 141 | UNSAFE.freeMemory(address); 142 | 143 | if (debugMode) { 144 | 145 | allocs.remove(address); 146 | 147 | /* String callerInfo = formatCallerInfo(new Throwable().fillInStackTrace().getStackTrace()) 148 | 149 | System.out.println("FREED " + address + ", bytes: " + size + ", caller: " + callerInfo); 150 | */ 151 | 152 | } 153 | 154 | } 155 | 156 | public static void addAllocOffHeapMemToGarbage(long address, long size) { 157 | 158 | synchronized(garbage) { 159 | 160 | garbage.add(new long[]{address, size}); 161 | 162 | } 163 | } 164 | 165 | public static void freeGarbageOffHeapMem(long targetMinBytesToFree) { 166 | 167 | synchronized(garbage) { 168 | 169 | long freed = 0l; 170 | 171 | if (targetMinBytesToFree > 0l && !garbage.isEmpty()) { 172 | 173 | if(debugMode) System.out.println("\nOff-heap garbage collection..."); 174 | 175 | long f = 0l; 176 | 177 | do { 178 | 179 | long[] entry = garbage.pop/*poll*/(); 180 | 181 | if(entry != null) { 182 | 183 | freeOffHeapMem(entry[0], entry[1]); 184 | 185 | f += entry[1]; 186 | 187 | } 188 | 189 | } while (freed < targetMinBytesToFree && !garbage.isEmpty()); 190 | 191 | System.out.println("\n\u001B[35mOff-heap garbage collection complete. Freed " + f + " bytes (" + round(Float.valueOf(f) / 1073741824, 4) + " G)\u001B[0m"); 192 | 193 | if (debugMode) 194 | diffSAT.stats().writeEntry("offheapGarbageFreedBytes", f, 0, false); 195 | 196 | } 197 | 198 | } 199 | 200 | } 201 | 202 | public static long allocatedUnsafeMemory() { 203 | 204 | return allocatedOffHeapDiffSATGlobal.get(); 205 | 206 | } 207 | 208 | /** 209 | * Accuracy depends on estimatedAvailableForUnsafe. 210 | * 211 | * @return Rough estimation of space originally (before any unsafe or direct buffer etc allocations) available for native allocations 212 | */ 213 | public static long approxSizeOfInitialFreeUnsafeMemory() { 214 | 215 | return estimatedAvailableForUnsafe; 216 | 217 | } 218 | 219 | /** 220 | * Accuracy depends on estimatedAvailableForUnsafe. If this value is too low, we might even get a negative result. 221 | * 222 | * @return Rough estimation of space currently available for new native allocations 223 | */ 224 | public static long approxSizeOfCurrentFreeUnsafeMemory() { 225 | 226 | return approxSizeOfInitialFreeUnsafeMemory() - allocatedUnsafeMemory(); 227 | 228 | } 229 | 230 | /** 231 | * For debugging only. Don't call in code which uses this class concurrently 232 | */ 233 | public static void resetMemTracerDebug() { 234 | 235 | allocatedOffHeapDiffSATGlobal.set(0l); 236 | 237 | if (debugMode) { 238 | 239 | allocs.clear(); 240 | 241 | } 242 | 243 | } 244 | 245 | /** 246 | * For debugging, to find off-heap memory leaks 247 | */ 248 | public static void showRemainingAllocsDebug() { 249 | 250 | if (debugMode) { 251 | 252 | if(allocs.isEmpty()) 253 | System.out.println("No remaining items in off-heap garbage list for debugging"); 254 | else allocs.forEach((aLong, s) -> { 255 | 256 | if (true || !s.contains("LeanUS")) 257 | 258 | System.out.println("REMAINING address (off-heap memory not freed by garbage collection):\n" + aLong + ", caller: " + s); 259 | 260 | }); 261 | 262 | } 263 | 264 | } 265 | 266 | } 267 | -------------------------------------------------------------------------------- /src/main/scala/input/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Find User API entry point classes [[input.ProbabilisticAnswerSetProgram]] (API for plain or probabilistic Answer Set Programming) and [[input.BooleanFormulaWithCosts]] (API for plain or probabilistic SAT solving) here 3 | */ 4 | package object input { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /src/main/scala/solving/NogoodReduciblesSequence.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018,2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package solving 13 | 14 | import it.unimi.dsi.fastutil.longs.LongArrayList 15 | 16 | 17 | @deprecated class NogoodReduciblesSequence(capacity: Int) extends LongArrayList(capacity) { // consider using NogoodReduciblesSequenceUnsafe instead 18 | 19 | type NogoodReducible = Long 20 | 21 | @inline def clearUS(): Unit = size = 0 22 | 23 | @inline def cutoffUS(whereExclusive: Int): Unit = size = whereExclusive 24 | 25 | @inline def getUS(index: Int): NogoodReducible = a(index) 26 | 27 | @inline def removeUS(k: NogoodReducible): Unit = { 28 | 29 | this.rem(k) 30 | 31 | } 32 | 33 | @inline def removeByIndexUS(i: Int): Unit = { 34 | 35 | if(i < size - 1) 36 | set(i, getUS(size - 1)) 37 | 38 | size -= 1 39 | 40 | } 41 | 42 | @inline def addUS(k: NogoodReducible): Unit = { 43 | 44 | if (size >= a.length) { 45 | 46 | val t = new Array[NogoodReducible](/*(a.length << 1) + 1*/a.length + 128) 47 | 48 | if(size > 0) 49 | System.arraycopy(a, 0, t, 0, size) 50 | 51 | a = t.asInstanceOf[Array[NogoodReducible]] 52 | 53 | } 54 | 55 | a.update(size, k) 56 | 57 | size += 1 58 | 59 | } 60 | 61 | @inline def getArray: Array[NogoodReducible] = a 62 | 63 | @inline def count(item: NogoodReducible) = { 64 | 65 | var c = 0 66 | 67 | var i = 0 68 | 69 | while(i < size) { 70 | 71 | if(getUS(i) == item) 72 | c += 1 73 | 74 | i += 1 75 | 76 | } 77 | 78 | c 79 | 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /src/main/scala/solving/NogoodReduciblesSequenceUnsafe.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018, 2020 by Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package solving 13 | 14 | import input.UNSAFEhelper.{addAllocOffHeapMemToGarbage, allocateOffHeapMem, _} 15 | 16 | import it.unimi.dsi.fastutil.longs.LongOpenHashSet 17 | 18 | import sharedDefs._ 19 | 20 | import scala.collection.mutable.ArrayBuffer 21 | 22 | class NogoodReduciblesSequenceUnsafe(initialCapacity: Int) { 23 | 24 | type NogoodReducible = Long 25 | 26 | private[this] val bytesPerElement = 8 /*<-- reducible:Long*/ 27 | // + 4/*<-- blocking eli:Int*/ 28 | // To activate blocker elis, add 4 to slot (see above), look for other mentions of "blocker" in this class, and in BCP 29 | // omit all reducible list entries for which !isNegSetInPass(UNSAFE.getInt(addrOfItemInRedList + 8l)). 30 | // But doesn't seem to have any benefit in preliminary experiments. 31 | 32 | private[this] var noOfAvailableSlotsForItems = initialCapacity 33 | 34 | private[this] var a: Long = allocateOffHeapMem(allocatedBytes(noOfAvailableSlotsForItems)) //new LongArrayUnsafeS(sizev = initialCapacity) 35 | 36 | private[this] var sizev = 0 37 | 38 | private[this] var cachedHashSet: LongOpenHashSet = null.asInstanceOf[LongOpenHashSet] 39 | 40 | //private[this] var sortings = 0 41 | private[this] var traversalDirectionUpwards = true 42 | 43 | @inline def setTraversalDirection(direction: Boolean) = traversalDirectionUpwards = direction 44 | 45 | @inline def allocatedBytes(allocatedItems: Int = noOfAvailableSlotsForItems): Int = 46 | allocatedItems * bytesPerElement 47 | 48 | @inline def addToGarbage(): Unit = { 49 | 50 | addAllocOffHeapMemToGarbage(a, allocatedBytes(noOfAvailableSlotsForItems)) //a.addToGarbage() 51 | 52 | } 53 | 54 | @inline def clearUS(): Unit = sizev = 0 55 | 56 | @inline def cutoffUS(whereExclusive: Int): Unit = sizev = whereExclusive 57 | 58 | @inline def getAddr: Long = a 59 | 60 | @inline def getBytesPerElement: Int = bytesPerElement 61 | 62 | @inline def getAddrOfItem(index: Int): Long = a + index * bytesPerElement // TODO: make shiftable? 63 | 64 | @inline def getReducibleUS(index: Int): NogoodReducible = UNSAFE.getLong(getAddrOfItem(index)) 65 | 66 | @inline def removeByIndexUS(i: Int): Unit = { 67 | 68 | sizev -= 1 69 | 70 | if (i < sizev) { 71 | 72 | UNSAFE.copyMemory(getAddrOfItem(sizev), getAddrOfItem(i), bytesPerElement) 73 | 74 | } 75 | 76 | } 77 | 78 | @inline def removeByAddrUS(iAddr: Long): Unit = { 79 | 80 | sizev -= 1 81 | 82 | UNSAFE.copyMemory(getAddrOfItem(sizev), iAddr, bytesPerElement) 83 | 84 | } 85 | 86 | @inline def addReducibleUS(reducible: NogoodReducible): Unit = { 87 | 88 | if (sizev >= noOfAvailableSlotsForItems) { // should occur only rarely if we chose the initial size appropriately, however 89 | // it also depends on the number of learned nogoods which is difficult to predict. 90 | 91 | val newAvailableNoOfSlotsForItems = noOfAvailableSlotsForItems << 1 // sizev.get + incIfOverflow 92 | 93 | a = resizeOffHeapMem(a, allocatedBytes(noOfAvailableSlotsForItems), 94 | allocatedBytes(newAvailableNoOfSlotsForItems)) 95 | 96 | noOfAvailableSlotsForItems = newAvailableNoOfSlotsForItems 97 | 98 | } 99 | 100 | UNSAFE.putLong(getAddrOfItem(sizev), reducible) 101 | 102 | sizev += 1 103 | 104 | if(keepNogoodsWeaklySorted && sizev % 32 == 0) { 105 | 106 | if(traversalDirectionUpwards) 107 | sortByInplace(red => -UNSAFE.getInt(red)/*=size of nogood*//*-UNSAFE.getFloat(red + ((3 + 4 - 4) << 2))*/, sizev) 108 | else 109 | sortByInplace(red => UNSAFE.getInt(red)/*UNSAFE.getFloat(red + ((3 + 4 - 4) << 2))*/, sizev) 110 | 111 | } 112 | 113 | } 114 | 115 | def sortByInplace(by: NogoodReducible => Float, until: Int): Unit = { 116 | 117 | // insertion sort - use only if array is very small (but then it's fast): 118 | 119 | var j = 1 120 | 121 | var key = -1l 122 | 123 | var i = -1 124 | 125 | while (j < until) { 126 | 127 | key = getReducibleUS(j) 128 | 129 | i = j - 1 130 | 131 | //println(by(key)) 132 | 133 | while (i > -1 && by(getReducibleUS(i)) > by(key)) { 134 | 135 | updateItemUS(i + 1, getReducibleUS(i)) 136 | 137 | i -= 1 138 | 139 | } 140 | 141 | updateItemUS(i + 1, key) 142 | 143 | j += 1 144 | 145 | } 146 | 147 | } 148 | 149 | 150 | @inline def updateItemUS(index: Int, reducible: NogoodReducible) = { 151 | 152 | UNSAFE.putLong(getAddrOfItem(index), reducible) 153 | 154 | } 155 | 156 | 157 | @inline def swap(i: Int, j: Int, valueI: Long): Unit = { 158 | 159 | updateItemUS(i, getReducibleUS(j)) // if blockingElis are enabled, modify accordingly 160 | 161 | updateItemUS(j, valueI) 162 | 163 | } 164 | 165 | @inline def toArrayBufferOfReducibles: ArrayBuffer[NogoodReducible] = { 166 | 167 | val ab = ArrayBuffer[NogoodReducible]() 168 | 169 | var i = 0 170 | 171 | while (i < sizev) { 172 | 173 | ab.append(getReducibleUS(i)) 174 | 175 | i += 1 176 | 177 | } 178 | 179 | ab 180 | 181 | } 182 | 183 | @inline def findReducibleUS(item: NogoodReducible): Int = { 184 | 185 | var i = 0 186 | 187 | while (i < sizev) { 188 | 189 | if (getReducibleUS(i) == item) 190 | return i 191 | 192 | i += 1 193 | 194 | } 195 | 196 | -1 197 | 198 | } 199 | 200 | @inline def containsReducible(item: NogoodReducible): Boolean = { 201 | 202 | findReducibleUS(item) != -1 203 | 204 | } 205 | 206 | @inline def toHashSetOfReducibles: LongOpenHashSet = { 207 | 208 | val hs = new LongOpenHashSet(64) 209 | 210 | var i = 0 211 | 212 | while (i < sizev) { 213 | 214 | hs.add(getReducibleUS(i)) 215 | 216 | i += 1 217 | 218 | } 219 | 220 | cachedHashSet = hs 221 | 222 | hs 223 | 224 | } 225 | 226 | @inline def toHashSetOfReduciblesIncompl: LongOpenHashSet = { 227 | 228 | if (cachedHashSet == null) { 229 | 230 | toHashSetOfReducibles 231 | 232 | } else 233 | cachedHashSet 234 | 235 | } 236 | 237 | @inline def filterByReducibleUS(keepBy: NogoodReducible => Boolean): NogoodReduciblesSequenceUnsafe = { 238 | 239 | val ns = new NogoodReduciblesSequenceUnsafe(initialCapacity = sizev) 240 | 241 | var k = 0 242 | 243 | while (k < sizev) { 244 | 245 | if (keepBy(getReducibleUS(k))) 246 | ns.addReducibleUS(getReducibleUS(k)) 247 | 248 | k += 1 249 | 250 | } 251 | 252 | ns 253 | 254 | } 255 | 256 | @inline def countByReducibleUS(keepBy: NogoodReducible => Boolean): Int = { 257 | 258 | var c = 0 259 | 260 | var k = 0 261 | 262 | while (k < sizev) { 263 | 264 | if (keepBy(getReducibleUS(k))) 265 | c += 1 266 | 267 | k += 1 268 | 269 | } 270 | 271 | c 272 | 273 | } 274 | 275 | @inline def size(): Eli = sizev 276 | 277 | } -------------------------------------------------------------------------------- /src/main/scala/solving/SamplingResult.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018, 2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package solving 13 | 14 | import it.unimi.dsi.fastutil.ints.IntOpenHashSet 15 | 16 | import sharedDefs.Eli 17 | 18 | import scala.collection.mutable 19 | 20 | /** 21 | * The result of a sampler call 22 | * 23 | * @param modelsSymbolic Sequence of models represented as arrays of symbolic literals 24 | * @param modelsUsingElis Sequence of models represented as arrays of elis (literals represented as pos/neg Ints) and hash sets of elis 25 | * @param samplingDurationNs Sampling duration in nanoseconds 26 | */ 27 | case class SamplingResult(modelsSymbolic: mutable.Seq[Array[String]], 28 | modelsUsingElis: mutable.ArrayBuffer[(Array[Eli], IntOpenHashSet)], 29 | samplingDurationNs: Long) 30 | -------------------------------------------------------------------------------- /src/main/scala/solving/SharedAmongSingleSolverThreads.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018,2022 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details). 9 | * 10 | */ 11 | 12 | package solving 13 | 14 | import java.util.concurrent.ConcurrentHashMap 15 | import it.unimi.dsi.fastutil.longs.LongArrayList 16 | import sharedDefs._ 17 | import utils.{ByteArrayUnsafeS, FiniteQueue} 18 | /*import vmm.API 19 | import vmm.API.VMMType.PPMC 20 | import vmm.algs.PPMCPredictor */ 21 | 22 | class SharedAmongSingleSolverThreads(noOfAbsElis: Int) { 23 | 24 | var refreshedBestPhasesGlobal: Int = 0 25 | 26 | val bestPhasesQueueGlobal: FiniteQueue[ByteArrayUnsafeS] = if (globalBestPhaseMemo && 27 | (weakRephasingAtRestartEveryP.getAllAlternativeValues.find(_ > 0).isDefined || rephasePhaseMemoP.getAllAlternativeValues.contains(true))) // i.e., we are using a global array here. TODO: more tests whether thread-local is mostly better or not 28 | new FiniteQueue[ByteArrayUnsafeS](scala.collection.mutable.Queue[ByteArrayUnsafeS]()) 29 | else 30 | null 31 | 32 | var greedilyBestThread: Int = 0 33 | 34 | val fmStackGlobalCapacity: Int = 500000 35 | 36 | val fmStackGlobal: LongArrayList = if (freeOrReassignDeletedNogoodMemory && 37 | (freeDeletedNogoodMemoryApproach == 3)) new LongArrayList(fmStackGlobalCapacity) 38 | else 39 | null 40 | 41 | val nogoodReducibleExchangePoolSourceThreadsForCyclicSharingPrevention: ConcurrentHashMap[NogoodReducible, Int] = new java.util.concurrent.ConcurrentHashMap[NogoodReducible, Int /*producing thread*/ ]() 42 | 43 | val nogoodReducibleExchangePool: ConcurrentHashMap[NogoodReducible, Int] = 44 | /*TODO: use of this comes with heavy boxing*/ new java.util.concurrent.ConcurrentHashMap[NogoodReducible, Int /*producing thread*/ ]() 45 | 46 | val scoresFromSLS: ConcurrentHashMap[Eli, Float] = new java.util.concurrent.ConcurrentHashMap[Eli, Float]() 47 | 48 | //var minViolNogoodCountSum = Int.MaxValue 49 | 50 | var threadConfsOpt: Option[Array[SolverThreadSpecificSettings]] = None 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/scala/solving/SolverThreadSpecificSettings.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018, 2022 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package solving 13 | 14 | import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap 15 | 16 | import sharedDefs.Eli 17 | 18 | /** Includes only those settings which can be different across individual solver threads, including 19 | * all settings whose names end with ...P in sharedDefs.scala */ 20 | case class SolverThreadSpecificSettings(var threadNo: Int /*>=1*/ , 21 | positiveDependencyGraph: Int2ObjectOpenHashMap[List[Eli]], // TODO: move elsewhere 22 | assureProgIsTight: Boolean, 23 | var freeEliSearchApproach: Int, 24 | restartTriggerConfR: (Int, Int, Double), 25 | traverseReduciblesUpwardsInUnitProp: Boolean, 26 | initAbsElisArrangement: Int, 27 | prep: Preparation /*<-for debugging/crosschecks only*/ , 28 | seed: Long, 29 | restartFrequencyModifierFactorR: Float, 30 | useSLSinPhaseMemoRephasingR: Boolean, 31 | nogoodRemovalThreshRatio: Double, 32 | absEliScoringApproach: Int, 33 | nogoodRemovalThreshInit: Int, 34 | noisePhaseMemoR: Float, 35 | localRestarts: Boolean, 36 | scoringForRemovalOfLearnedNogoodsR: Int, 37 | weakRephasingAtRestartEvery: Int, 38 | rephasePhaseMemo: Boolean, 39 | nogoodBCPtraversalDirection: Int, 40 | absEliScoringApproach15NoOfBins: Int, 41 | var singleSolverThreadDataOpt: Option[SingleSolverThreadData]) 42 | -------------------------------------------------------------------------------- /src/main/scala/utils/ASPIOutils.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018, 2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | import java.io._ 13 | 14 | import sharedDefs.evalFactPrefix 15 | 16 | import scala.collection.{Set, mutable} 17 | import scala.language.postfixOps 18 | 19 | package object aspIOutils { 20 | 21 | type Pred = String 22 | 23 | type Model = Array[String] 24 | 25 | val parserInstanceCount = new java.util.concurrent.atomic.AtomicInteger(0) 26 | 27 | val auxPredPrefixBase = "__aux_" // prefixes for newly introduced atom symbols 28 | 29 | def isAuxAtom(pred: String) = pred.contains /*startsWith*/ (auxPredPrefixBase) // "contains" as pred could also be param(__aux...) 30 | 31 | /* The following kinds of auxiliary symbols are currently reserved: 32 | 33 | - auxiliary atoms used to desugar :- constraints (symbol contains _F_) 34 | - helper atoms in automatically generated spanning formulas (symbol contains _R_) 35 | - for other purposes (symbol contains _O_) 36 | 37 | */ 38 | 39 | def auxPredPrefix(kind: String) = auxPredPrefixBase + kind + "_" + parserInstanceCount.get() + "_" 40 | 41 | def newFalsePredsPrefix = auxPredPrefix("F") // prefixes for newly introduced auxiliary atoms for 42 | // desugaring :- integrity constraints (see aspif parser). 43 | 44 | def isFalsAuxAtom(pred: String) = pred.startsWith(auxPredPrefixBase) && pred.contains("F") 45 | 46 | def isSpanAuxAtom(pred: String) = isAuxAtom(pred) && pred.contains("R") 47 | 48 | def newLatentSymbolAuxAtomPrefix = auxPredPrefix("L") // prefixes for newly introduced auxiliary atom symbols for (re-)introduced atoms from certain #show aspif rules (see aspif parser). 49 | 50 | def newSpanSymbolAuxAtomPrefix = auxPredPrefix("R") // prefixes for newly introduced auxiliary atom symbols for (re-)introduced atoms from certain #show aspif rules (see aspif parser). 51 | 52 | def isLatentSymbolAuxAtom(pred: String) = pred.startsWith(auxPredPrefixBase) && pred.contains("L") 53 | 54 | // TODO(?): ^ these don't consider auxiliar variables introduced by the PCNF parser (probabilistic SAT) 55 | 56 | @inline def auxAtomSymbol(prefix: String, index: Int, encloseUncertainAuxWithParam: Boolean = false) = if (encloseUncertainAuxWithParam) 57 | "param_" + prefix + index else prefix + index 58 | 59 | def slurpFromInputStream(in: InputStream): String = { 60 | 61 | val byteArray = new Array[Byte](4096) 62 | 63 | val strBuf: StringBuilder = new StringBuilder() 64 | 65 | var bytesRead = 0 66 | 67 | while ( { 68 | 69 | bytesRead = in.read(byteArray, 0, byteArray.length) 70 | 71 | bytesRead != -1 72 | 73 | }) strBuf.append(new String(byteArray, 0, bytesRead)) 74 | 75 | strBuf.toString() 76 | 77 | } 78 | 79 | case class DisjRule(headPosAtoms: Set[Pred] = Set[Pred](), headNegAtoms: Set[Pred] = Set[Pred](), 80 | bodyPosAtoms: Set[Pred] = Set[Pred](), bodyNegAtoms: Set[Pred] = Set[Pred](), 81 | // ^ Which rule types are actually supported depends of course on solver and its preprocessor. 82 | // If multiple distinct head atoms are given, their semantics is disjunction. 83 | weight: (Double, Double) = (1d, 1d), 84 | var blit: Int = -1) { 85 | 86 | @deprecated override def toString = { 87 | 88 | val endDot = if (headPosAtoms.isEmpty || !headPosAtoms.head.contains('[')) "." else "" 89 | // ^ hack to allow for Clingo softconstraint weights within formulas (which have [weight ...] after the dot). 90 | 91 | val r = headPosAtoms.mkString(",") + headNegAtoms.map("not " + _).mkString(",") + (if (bodyPosAtoms.isEmpty && bodyNegAtoms.isEmpty) "" else " :- " + (bodyPosAtoms ++ bodyNegAtoms.map("not " + _)).mkString(",")) + endDot 92 | 93 | val rr = (if (weight != (1d, 1d)) 94 | "[" + weight._1 + ";" + weight._2 + "] " 95 | else 96 | "") + r 97 | 98 | rr.trim 99 | 100 | } 101 | 102 | } 103 | 104 | @inline def splitByRepChar(s: String, delim1: Char = ' ', delim2: Char = '\t'): Array[String] = { 105 | 106 | val ll = new mutable.ArrayBuilder.ofRef[String] 107 | 108 | var index = 0 109 | 110 | var i = 0 111 | 112 | val sl = s.length 113 | 114 | var inStr = false 115 | 116 | while (i < sl) { 117 | 118 | if(s.charAt(i) == '\"' && (i == 0 || s.charAt(i - 1) != '\\' || (i > 1 && s.charAt(i - 2) == '\\'))) { 119 | 120 | inStr = !inStr 121 | 122 | i += 1 123 | 124 | } else if (!inStr && s.charAt(i) == delim1 || s.charAt(i) == delim2) { 125 | 126 | ll.+=(s.substring(index, i)) 127 | 128 | i += 1 129 | 130 | while (s.charAt(i) == delim1 || s.charAt(i) == delim2) { // e.g., "token1 token2" gives ["token1", "token2"], not ["token1", " ", "token2"] 131 | 132 | i += 1 133 | 134 | } 135 | 136 | index = i 137 | 138 | } 139 | 140 | i += 1 141 | 142 | } 143 | 144 | if (s.last != delim1 && s.last != delim2) 145 | ll.+=(s.substring(index)) 146 | 147 | val r = ll.result() 148 | 149 | r 150 | 151 | } 152 | 153 | def splitEvalSymbol(sym: Pred) = { 154 | 155 | //val isTermQuoted = sym.startsWith(evalFactPrefix + "(\"") 156 | 157 | val endOfTermIndex = sym.indexOf(",\"?\"") 158 | 159 | // note that the term can be quoted or not 160 | 161 | val term = sym.take(endOfTermIndex).stripPrefix(evalFactPrefix).dropWhile(c => c == '(' || c == '\"').stripSuffix("\"").replaceAll("\\s", "") 162 | 163 | val remainder = sym.drop(endOfTermIndex + 4) 164 | 165 | (term, remainder) 166 | 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /src/main/scala/utils/ArrayValExtensibleIntUnsafe.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018, 2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package utils 13 | 14 | import sharedDefs.Eli 15 | 16 | /** 17 | * Unsafe low-level non-boxing replacement for a growable-only ArrayBuffer. Similar to ArrayBuilder, but 18 | * with random access and traversal. NB: couldn't avoid boxing in bytecode with 19 | * the generic [@specialized T (Int/Long)] variant (with Scala 2.13.1) 20 | * 21 | * @param buffer 22 | * @param initiallyOccupiedInBuffer 23 | */ 24 | class ArrayValExtensibleIntUnsafe(var buffer: IntArrayUnsafeS, initiallyOccupiedInBuffer: Int = 0) { 25 | 26 | var contentLen = if (initiallyOccupiedInBuffer == -1) buffer.size else initiallyOccupiedInBuffer 27 | 28 | @inline def getContent: Array[Int] = { 29 | 30 | buffer.toArray.take(contentLen) 31 | 32 | } 33 | 34 | @inline def getContentUnsafe: IntArrayUnsafeS = { 35 | 36 | buffer.cloneToNew(padding = 0, keep = contentLen, cutOff = true) 37 | 38 | } 39 | 40 | @inline def length: Int = contentLen 41 | 42 | @inline def bufferSize: Int = buffer.size 43 | 44 | @inline def get(index: Int): Int = buffer.get(index) 45 | 46 | @inline def get(index: Long): Int = buffer.get(index) 47 | 48 | @inline def update(index: Int, value: Int): Unit = buffer.update(index, value) 49 | 50 | @inline def popLast: Int = { 51 | 52 | contentLen -= 1 53 | 54 | get(contentLen) 55 | 56 | } 57 | 58 | @inline def removeByIndex(index: Int): Unit = { 59 | 60 | if (index < contentLen - 1) 61 | update(index, popLast) 62 | else 63 | contentLen -= 1 64 | 65 | } 66 | 67 | @inline def removeByItem(item: Int): Unit = { 68 | 69 | removeByIndex(indexOf(item)) 70 | 71 | } 72 | 73 | @inline def isEmpty: Boolean = contentLen == 0 74 | 75 | @inline def traverseUntil(itemOp: Int => Boolean): Unit = { 76 | 77 | var i = 0 78 | 79 | var stop = false 80 | 81 | while (!stop) { 82 | 83 | stop = i >= contentLen || itemOp(buffer.get(i)) 84 | 85 | i += 1 86 | 87 | } 88 | 89 | } 90 | 91 | @inline def contains(item: Int, until: Int): Boolean = { 92 | 93 | var i = until - 1 94 | 95 | while (i >= 0) { 96 | 97 | if (buffer.get(i) == item) 98 | return true 99 | 100 | i -= 1 101 | 102 | } 103 | 104 | false 105 | 106 | } 107 | 108 | @inline def containsBy(item: Int, until: Int, by: Int => Int): Boolean = { 109 | 110 | var i = until - 1 111 | 112 | while (i >= 0) { 113 | 114 | if (by(buffer.get(i)) == by(item)) 115 | return true 116 | 117 | i -= 1 118 | 119 | } 120 | 121 | false 122 | 123 | } 124 | 125 | @inline def indexOf(item: Int): Int = { 126 | 127 | var i = 0 128 | 129 | while (i < size) { 130 | 131 | if (buffer.get(i) == item) 132 | return i 133 | 134 | i += 1 135 | 136 | } 137 | 138 | -1 139 | 140 | } 141 | 142 | @inline def append(item: Int): Unit = { 143 | 144 | if (contentLen >= buffer.size) { 145 | 146 | val bufferOld = buffer 147 | 148 | val additionalSpace = contentLen >> 1 149 | 150 | buffer = buffer.cloneToNew(padding = additionalSpace, keep = contentLen, cutOff = false) 151 | 152 | if (buffer.getAddr != bufferOld.getAddr) { 153 | 154 | bufferOld.free() // (alternatively we could add the old buffer address to the UNSAFEhelper garbage) 155 | 156 | } 157 | 158 | } 159 | 160 | buffer.update(contentLen, item) 161 | 162 | contentLen += 1 163 | 164 | } 165 | 166 | @inline def appendNoExpansion(item: Int): Boolean = { 167 | 168 | (contentLen < buffer.size) && { 169 | 170 | buffer.update(contentLen, item) 171 | 172 | contentLen += 1 173 | 174 | true 175 | 176 | } 177 | 178 | } 179 | 180 | @inline def clear(): Unit = contentLen = 0 181 | 182 | @inline def size(): Eli = contentLen 183 | 184 | @inline def freeBuffer(): Unit = buffer.free() 185 | 186 | @inline def addToGarbageBuffer(): Unit = buffer.addToGarbage() 187 | 188 | } -------------------------------------------------------------------------------- /src/main/scala/utils/ArrayValExtensibleLongUnsafe.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018, 2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package utils 13 | 14 | import sharedDefs.Eli 15 | 16 | /** 17 | * Unsafe low-level non-boxing replacement for a growable-only ArrayBuffer. Similar to ArrayBuilder, but 18 | * with random access and traversal. 19 | * 20 | * @param buffer 21 | * @param initiallyOccupiedInBuffer 22 | */ 23 | class ArrayValExtensibleLongUnsafe(var buffer: LongArrayUnsafeS, initiallyOccupiedInBuffer: Int = 0) { 24 | 25 | var contentLen = if (initiallyOccupiedInBuffer == -1) buffer.size else initiallyOccupiedInBuffer 26 | 27 | @inline def bufferSize: Int = buffer.size 28 | 29 | @inline def get(index: Int): Long = buffer.get(index) 30 | 31 | @inline def get(index: Long): Long = buffer.get(index) 32 | 33 | @inline def update(index: Int, value: Long): Unit = buffer.update(index, value) 34 | 35 | @inline def popLast: Long = { 36 | 37 | contentLen -= 1 38 | 39 | get(contentLen) 40 | 41 | } 42 | 43 | @inline def removeByIndex(index: Int): Unit = { 44 | 45 | if (index < contentLen - 1) 46 | update(index, popLast) 47 | else 48 | contentLen -= 1 49 | 50 | } 51 | 52 | @inline def removeByItem(item: Long): Unit = { 53 | 54 | removeByIndex(indexOf(item)) 55 | 56 | } 57 | 58 | @inline def isEmpty: Boolean = contentLen == 0 59 | 60 | @inline def setSize(newSize: Int): Unit = contentLen = newSize 61 | 62 | @inline def traverseUntil(itemOp: Long => Boolean): Unit = { 63 | 64 | var i = 0 65 | 66 | var stop = false 67 | 68 | while (!stop) { 69 | 70 | stop = i >= contentLen || itemOp(buffer.get(i)) 71 | 72 | i += 1 73 | 74 | } 75 | 76 | } 77 | 78 | @inline def contains(item: Long): Boolean = contains(item, contentLen) 79 | 80 | @inline def contains(item: Long, until: Int): Boolean = { 81 | 82 | var i = until - 1 83 | 84 | while (i >= 0) { 85 | 86 | if (buffer.get(i) == item) 87 | return true 88 | 89 | i -= 1 90 | 91 | } 92 | 93 | false 94 | 95 | } 96 | 97 | @inline def containsBy(item: Long, until: Int, by: Long => Int): Boolean = { 98 | 99 | var i = until - 1 100 | 101 | while (i >= 0) { 102 | 103 | if (by(buffer.get(i)) == by(item)) 104 | return true 105 | 106 | i -= 1 107 | 108 | } 109 | 110 | false 111 | 112 | } 113 | 114 | @inline def indexOf(item: Long): Int = { 115 | 116 | var i = 0 117 | 118 | while (i < size) { 119 | 120 | if (buffer.get(i) == item) 121 | return i 122 | 123 | i += 1 124 | 125 | } 126 | 127 | -1 128 | 129 | } 130 | 131 | @inline def append(item: Long): Unit = { 132 | 133 | if (contentLen >= buffer.size) { 134 | 135 | val bufferOldAddr = buffer.getAddr 136 | 137 | val additionalSpace = contentLen >> 1 138 | 139 | buffer = buffer.cloneToNew(padding = additionalSpace, contentLen, cutOff = false) 140 | 141 | if (buffer.getAddr != bufferOldAddr) { 142 | 143 | input.UNSAFEhelper.UNSAFE.freeMemory(bufferOldAddr) // (alternatively we could add the old buffer address to the UNSAFEhelper garbage) 144 | 145 | } 146 | 147 | } 148 | 149 | buffer.update(contentLen, item) 150 | 151 | contentLen += 1 152 | 153 | } 154 | 155 | @inline def appendNoExpansion(item: Long): Boolean = { 156 | 157 | (contentLen < buffer.size) && { 158 | 159 | buffer.update(contentLen, item) 160 | 161 | contentLen += 1 162 | 163 | true 164 | 165 | } 166 | 167 | } 168 | 169 | @inline def clear(): Unit = contentLen = 0 170 | 171 | @inline def size(): Int = contentLen 172 | 173 | @inline def sort(by: Long => Double): Unit = { 174 | 175 | buffer.sortByInplace(by = by, until = contentLen) 176 | 177 | } 178 | 179 | @inline def freeBuffer(): Unit = buffer.free() 180 | 181 | @inline def addToGarbageBuffer(): Unit = buffer.addToGarbage() 182 | 183 | } -------------------------------------------------------------------------------- /src/main/scala/utils/ByteArrayUnsafeS.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018,2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package utils 13 | 14 | import input.UNSAFEhelper._ 15 | import sun.misc.Unsafe 16 | 17 | /** This is not a general-purpose unsafe (off-heap) byte array class - designed for use in project diff-SAT only! */ 18 | object ByteArrayUnsafeS { 19 | 20 | //private[this] var unsafe: Unsafe = null 21 | 22 | //var alignment = 128 //UNSAFE.pageSize 23 | 24 | //var internalPaddingFact = 1 // multiple of actual alignment (see below) 25 | 26 | var byteArrayOffset = -1l 27 | 28 | def init(us: Unsafe): Unit = { 29 | 30 | byteArrayOffset = UNSAFE.arrayBaseOffset(classOf[Array[Byte]]) 31 | 32 | } 33 | 34 | @inline def getUnsafe: Unsafe = UNSAFE 35 | 36 | } 37 | 38 | /** This is not a general-purpose unsafe byte array class - designed for use in project diff-SAT only! */ 39 | class ByteArrayUnsafeS(var sizev: Int /*, aligned: Boolean*/) { 40 | 41 | //var isSorted: Boolean = false 42 | 43 | private[this] var addr: Long = 0L 44 | 45 | //private[this] val unsafe = ByteArrayUnsafeS.getUnsafe 46 | 47 | private[this] val stringBuilder: java.lang.StringBuilder = new java.lang.StringBuilder(sizev) 48 | 49 | addr = allocateOffHeapMem(sizev) /*{ 50 | 51 | val alignment = 64 52 | 53 | addr = /*UNSAFE.allocateMemory*/allocateOffHeapMem((sizev) + alignment + alignment * 8/*ByteArrayUnsafeS.internalPaddingFact*/) 54 | 55 | if (alignment > 0l && (addr & (alignment - 1l)) != 0) 56 | addr += (alignment - (addr & (alignment - 1))) 57 | 58 | addr += alignment * 8/*ByteArrayUnsafeS.internalPaddingFact*/ 59 | 60 | }*/ 61 | 62 | private[this] val addrMid: Long = addr + ((sizev >> 1)) 63 | 64 | 65 | @inline def this(values: Array[Byte]) = { 66 | 67 | this(sizev = values.length) 68 | 69 | setFromArray(values) 70 | 71 | } 72 | 73 | @inline def this(s: Int, initialValue: Byte /*, aligned: Boolean*/) = { 74 | 75 | this(sizev = s /*, aligned = aligned*/) 76 | 77 | fill(initialValue) 78 | 79 | } 80 | 81 | @inline def fill(value: Byte, length: Int = sizev): Unit = { 82 | 83 | UNSAFE.setMemory(addr, length, value) 84 | 85 | } 86 | 87 | @inline def free(): Unit = freeOffHeapMem(addr, sizev) //UNSAFE.freeMemory(addr) 88 | 89 | @inline def addToGarbage(): Unit = addAllocOffHeapMemToGarbage(addr, sizev) 90 | 91 | @inline def size(): Int = sizev 92 | 93 | @inline override def hashCode(): Int = { 94 | 95 | var h = 1 96 | 97 | var i = 0 98 | 99 | while (i < sizev) { 100 | 101 | h = 31 * h + get(i) 102 | 103 | i += 1 104 | 105 | } 106 | 107 | h 108 | 109 | } 110 | 111 | @inline override def equals(obj: Any): Boolean = { 112 | 113 | if (obj.isInstanceOf[ByteArrayUnsafeS] && obj != null) 114 | this.hashCode == obj.asInstanceOf[ByteArrayUnsafeS].hashCode 115 | else 116 | super.equals(obj) 117 | 118 | } 119 | 120 | @inline def setFromArray(values: Array[Byte]): Unit = { 121 | 122 | UNSAFE.copyMemory(values, ByteArrayUnsafeS.byteArrayOffset, null, addr, values.length) 123 | 124 | } 125 | 126 | @inline def setFromUnsafeByteArray(values: ByteArrayUnsafeS): Unit = { 127 | 128 | UNSAFE.copyMemory(values, 0l, null, addr, values.sizev) 129 | 130 | } 131 | 132 | @inline def get(index: Int): Byte = { 133 | 134 | UNSAFE.getByte(addr + index) 135 | 136 | } 137 | 138 | @inline def getMid(index: Int): Byte = { 139 | 140 | UNSAFE.getByte(addrMid + index) 141 | 142 | } 143 | 144 | @inline def get(index: Long): Byte = { 145 | 146 | UNSAFE.getByte(addr + index) 147 | 148 | } 149 | 150 | @inline def getBoolean(index: Int): Boolean = { 151 | 152 | UNSAFE.getByte(addr + index) != 0x00.toByte // NB: caller specific. There is no "norm" for how to represent Booleans as bytes 153 | 154 | } 155 | 156 | @inline def first: Byte = { 157 | 158 | UNSAFE.getByte(addr) 159 | 160 | } 161 | 162 | @inline def update(index: Int, value: Byte): Unit = { 163 | 164 | UNSAFE.putByte(addr + index, value) 165 | 166 | } 167 | 168 | @inline def updateMid(index: Int, value: Byte): Unit = { 169 | 170 | UNSAFE.putByte(addrMid + index, value) 171 | 172 | } 173 | 174 | @inline def update(index: Long, value: Byte): Unit = { 175 | 176 | UNSAFE.putByte(addr + index, value) 177 | 178 | } 179 | 180 | /* 181 | @inline def update(index: Int, value: Boolean): Unit = { 182 | 183 | UNSAFE.putByte(addr + index, if (value) 0xFF.toByte else 0x00.toByte) // NB: caller specific. There is no "norm" for how to represent Booleans as bytes 184 | 185 | } 186 | 187 | 188 | @inline def updateMid(index: Int, value: Boolean): Unit = { 189 | 190 | UNSAFE.putByte(addrMid + index, if (value) 0xFF.toByte else 0x00.toByte) // NB: caller specific. There is no "norm" for how to represent Booleans as bytes 191 | 192 | }*/ 193 | 194 | 195 | @inline def toArray: Array[Byte] = { 196 | 197 | val array = new Array[Byte](sizev) 198 | 199 | UNSAFE.copyMemory(null, addr, array, ByteArrayUnsafeS.byteArrayOffset, sizev) 200 | 201 | array 202 | 203 | } 204 | 205 | @inline def toArray(l: Int): Array[Byte] = { 206 | 207 | val array = new Array[Byte](l) 208 | 209 | UNSAFE.copyMemory(null, addr, array, ByteArrayUnsafeS.byteArrayOffset, l) 210 | 211 | array 212 | 213 | } 214 | 215 | @inline def toArray(from: Int, until: Int): Array[Byte] = { 216 | 217 | val array = new Array[Byte](until - from) 218 | 219 | UNSAFE.copyMemory(null, addr + (from), array, ByteArrayUnsafeS.byteArrayOffset, (until - from)) 220 | 221 | array 222 | 223 | } 224 | 225 | @inline override def toString: String = { 226 | 227 | val s: StringBuilder = new StringBuilder 228 | 229 | var i = 0 230 | 231 | while (i < sizev) { 232 | 233 | s.append(if (i < sizev - 1) 234 | get(i) + "," 235 | else 236 | get(i)) { 237 | 238 | i += 1 239 | 240 | i - 1 241 | 242 | } 243 | 244 | } 245 | 246 | s.toString 247 | 248 | } 249 | 250 | @inline def toBitString: String = { 251 | 252 | stringBuilder.setLength(0) 253 | 254 | var i = 0 255 | 256 | while (i < sizev) { 257 | 258 | stringBuilder.append( 259 | if (get(i) != 0x00.toByte) 260 | '1' 261 | else 262 | '0' 263 | ) 264 | 265 | i += 1 266 | 267 | } 268 | 269 | stringBuilder.toString 270 | 271 | } 272 | 273 | @inline def toBitStringLocal: String = { 274 | 275 | val stringBuilder: java.lang.StringBuilder = new java.lang.StringBuilder(sizev) 276 | 277 | var i = 0 278 | 279 | while (i < sizev) { 280 | 281 | stringBuilder.append( 282 | if (get(i) != 0x00.toByte) 283 | '1' 284 | else 285 | '0' 286 | ) 287 | 288 | i += 1 289 | 290 | } 291 | 292 | stringBuilder.toString 293 | 294 | } 295 | 296 | @inline def getAddr: Long = addr 297 | 298 | @inline def clone(padding: Int = 0): ByteArrayUnsafeS = { 299 | 300 | val bau: ByteArrayUnsafeS = new ByteArrayUnsafeS(sizev + padding /*, aligned = aligned*/) 301 | 302 | UNSAFE.copyMemory(addr, bau.getAddr, sizev) 303 | 304 | bau 305 | 306 | } 307 | 308 | } 309 | -------------------------------------------------------------------------------- /src/main/scala/utils/DoubleArrayUnsafeS.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018,2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package utils 13 | 14 | import sun.misc.Unsafe 15 | import input.UNSAFEhelper._ 16 | 17 | /** This is not general-purpose code for using unsafe memory - designed for use in project diff-SAT only! */ 18 | object DoubleArrayUnsafeS { 19 | 20 | //private[this] var unsafe: Unsafe = null 21 | 22 | var alignment = 0 23 | 24 | var internalPaddingFact = 0 // multiple of actual alignment (see below) 25 | 26 | var doubleArrayOffs = -1 27 | 28 | @inline def init(us: Unsafe): Unit = { 29 | 30 | //unsafe = us 31 | 32 | doubleArrayOffs = UNSAFE.arrayBaseOffset(classOf[Array[Double]]) 33 | 34 | } 35 | 36 | @inline def getUnsafe: Unsafe = UNSAFE 37 | 38 | } 39 | 40 | /** This is not a general-purpose unsafe array class - designed for use in project diff-SAT only! */ 41 | class DoubleArrayUnsafeS(var sizev: Int, aligned: Boolean) { 42 | 43 | //private[this] val unsafe = ByteArrayUnsafeS.getUnsafe // without private[this] the field access in the bytecode would be by invokevirtual 44 | 45 | private[this] var addr: Long = 0L 46 | 47 | private[this] var allocated: Long = 0l 48 | 49 | if (DoubleArrayUnsafeS.alignment == 0) { 50 | 51 | allocated = sizev << 3 52 | 53 | addr = allocateOffHeapMem(allocated) 54 | 55 | } else { 56 | 57 | allocated = (sizev << 3) + DoubleArrayUnsafeS.alignment + 58 | DoubleArrayUnsafeS.alignment * DoubleArrayUnsafeS.internalPaddingFact 59 | 60 | addr = allocateOffHeapMem(allocated) 61 | 62 | if (DoubleArrayUnsafeS.alignment > 0l && (addr & (DoubleArrayUnsafeS.alignment - 1l)) != 0) 63 | addr += (DoubleArrayUnsafeS.alignment - (addr & (DoubleArrayUnsafeS.alignment - 1))) 64 | 65 | addr += DoubleArrayUnsafeS.alignment * DoubleArrayUnsafeS.internalPaddingFact 66 | 67 | } 68 | 69 | @inline def this(values: Array[Double], aligned: Boolean) { 70 | 71 | this(sizev = values.length, aligned = aligned) 72 | 73 | setFromArray(values) 74 | 75 | } 76 | 77 | @inline def this(s: Int, initValue: Double, aligned: Boolean) { 78 | 79 | this(sizev = s, aligned = aligned) 80 | 81 | var i = 0 82 | 83 | while(i < s) { 84 | 85 | update(i, initValue) 86 | 87 | i += 1 88 | 89 | } 90 | 91 | } 92 | 93 | @inline def free(): Unit = freeOffHeapMem(addr, allocated) 94 | 95 | @inline def size(): Int = sizev 96 | 97 | @inline def setFromArray(values: Array[Double]): Unit = { 98 | 99 | UNSAFE.copyMemory(values, DoubleArrayUnsafeS.doubleArrayOffs, null, addr, values.length << 3) 100 | 101 | } 102 | 103 | @inline def get(index: Int): Double = { 104 | 105 | UNSAFE.getDouble(addr + (index << 3)) 106 | 107 | } 108 | 109 | @inline def get(index: Long): Double = { 110 | 111 | UNSAFE.getDouble(addr + (index << 3)) 112 | 113 | } 114 | 115 | @inline def first: Double = { 116 | 117 | UNSAFE.getDouble(addr) 118 | 119 | } 120 | 121 | @inline def update(index: Int, value: Double): Unit = { 122 | 123 | UNSAFE.putDouble(addr + (index << 3), value) 124 | 125 | } 126 | 127 | @inline def update(index: Long, value: Double): Unit = { 128 | 129 | UNSAFE.putDouble(addr + (index << 3), value) 130 | 131 | } 132 | 133 | @inline final def incBy(index: Int, by: Double): Double = { 134 | 135 | val newValue = UNSAFE.getDouble(addr + (index << 3)) + by 136 | 137 | UNSAFE.putDouble(addr + (index << 3), newValue) 138 | 139 | newValue 140 | 141 | } 142 | 143 | @inline final def mulBy(index: Int, by: Double): Unit = { 144 | 145 | UNSAFE.putDouble(addr + (index << 3), UNSAFE.getDouble(addr + (index << 3)) * by) 146 | 147 | } 148 | 149 | @inline def toArray: Array[Double] = { 150 | 151 | val array = new Array[Double](sizev) 152 | 153 | UNSAFE.copyMemory(null, addr, array, DoubleArrayUnsafeS.doubleArrayOffs, sizev << 3) 154 | 155 | array 156 | 157 | } 158 | 159 | @inline def toArray(l: Int): Array[Double] = { 160 | 161 | val array = new Array[Double](l) 162 | 163 | UNSAFE.copyMemory(null, addr, array, DoubleArrayUnsafeS.doubleArrayOffs, l << 3) 164 | 165 | array 166 | 167 | } 168 | 169 | @inline def toArray(from: Int, until: Int): Array[Double] = { 170 | 171 | val array = new Array[Double](until - from) 172 | 173 | UNSAFE.copyMemory(null, addr + (from << 3), array, DoubleArrayUnsafeS.doubleArrayOffs, (until - from) << 3) 174 | 175 | array 176 | 177 | } 178 | 179 | override def toString: String = { 180 | 181 | val s: StringBuilder = new StringBuilder 182 | 183 | var i = 0 184 | 185 | while (i < sizev) { 186 | 187 | s.append(if (i < sizev - 1) 188 | get(i) + "," 189 | else 190 | get(i)) { 191 | 192 | i += 1 193 | 194 | i - 1 195 | 196 | } 197 | 198 | } 199 | 200 | s.toString 201 | 202 | } 203 | 204 | @inline def getAddr: Long = addr 205 | 206 | @inline def clone(padding: Int): DoubleArrayUnsafeS = { 207 | 208 | val bau: DoubleArrayUnsafeS = new DoubleArrayUnsafeS(sizev + padding, aligned = aligned) 209 | 210 | UNSAFE.copyMemory(addr, bau.getAddr, sizev << 3) 211 | 212 | bau 213 | 214 | } 215 | 216 | } 217 | -------------------------------------------------------------------------------- /src/main/scala/utils/FiniteQueue.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018,2020 by Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package utils 13 | 14 | import sharedDefs.rngGlobal 15 | 16 | class FiniteQueue[A](q: scala.collection.mutable.Queue[A]) { 17 | 18 | @inline def enqueueFinite(elem: A, maxSize: Int) = { 19 | 20 | while (q.size >= maxSize) { 21 | q.dequeue 22 | } 23 | 24 | q.enqueue(elem) 25 | 26 | } 27 | 28 | @inline def front = q.front 29 | 30 | @inline def size = q.size 31 | 32 | @inline def isEmpty = q.isEmpty 33 | 34 | @inline def randomElement(limit: Int): A = { 35 | 36 | q.apply(rngGlobal.nextInt(limit)) 37 | 38 | } 39 | 40 | @inline def dequeue: A = q.dequeue() 41 | 42 | } -------------------------------------------------------------------------------- /src/main/scala/utils/FloatArrayUnsafeS.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018,2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package utils 13 | 14 | import sun.misc.Unsafe 15 | import input.UNSAFEhelper._ 16 | 17 | /** This is not a general-purpose unsafe class - designed for use in project diff-SAT only! */ 18 | object FloatArrayUnsafeS { 19 | 20 | //private[this] var unsafe: Unsafe = null 21 | 22 | var alignment = 128 // UNSAFE.pageSize Observe that unsafe's allocateMemory does some alignment (to value size) by itself 23 | 24 | var internalPaddingFact = 1 // multiple of actual alignment (see below) 25 | 26 | var floatArrayOffs = -1 27 | 28 | @inline def init(us: Unsafe): Unit = { 29 | 30 | //unsafe = us 31 | 32 | floatArrayOffs = UNSAFE.arrayBaseOffset(classOf[Array[Float]]) 33 | 34 | } 35 | 36 | @inline def getUnsafe: Unsafe = UNSAFE 37 | 38 | } 39 | 40 | /** This is not a general-purpose unsafe array class - designed for use in project diff-SAT only! */ 41 | class FloatArrayUnsafeS(var sizev: Int, aligned: Boolean) { 42 | 43 | //private[this] val unsafe = ByteArrayUnsafeS.getUnsafe // without private[this] the field access in the bytecode would be by invokevirtual 44 | 45 | private[this] var addr: Long = 0L 46 | 47 | private[this] var allocated: Long = 0l 48 | 49 | if (!aligned) { 50 | 51 | allocated = sizev << 2 52 | 53 | addr = allocateOffHeapMem(allocated) 54 | 55 | } else { 56 | 57 | allocated = (sizev << 2) + FloatArrayUnsafeS.alignment + 58 | FloatArrayUnsafeS.alignment * FloatArrayUnsafeS.internalPaddingFact 59 | 60 | addr = allocateOffHeapMem(allocated) 61 | 62 | if (FloatArrayUnsafeS.alignment > 0l && (addr & (FloatArrayUnsafeS.alignment - 1l)) != 0) 63 | addr += (FloatArrayUnsafeS.alignment - (addr & (FloatArrayUnsafeS.alignment - 1))) 64 | 65 | addr += FloatArrayUnsafeS.alignment * FloatArrayUnsafeS.internalPaddingFact 66 | 67 | } 68 | 69 | @inline def this(values: Array[Float], aligned: Boolean) { 70 | 71 | this(sizev = values.length, aligned = aligned) 72 | 73 | setFromArray(values) 74 | 75 | } 76 | 77 | @inline def this(s: Int, initValue: Float, aligned: Boolean) { 78 | 79 | this(sizev = s, aligned = aligned) 80 | 81 | var i = 0 82 | 83 | while(i < s) { 84 | 85 | update(i, initValue) 86 | 87 | i += 1 88 | 89 | } 90 | 91 | } 92 | 93 | @inline def free(): Unit = freeOffHeapMem(addr, allocated) 94 | 95 | @inline def size(): Int = sizev 96 | 97 | @inline def setFromArray(values: Array[Float]): Unit = { 98 | 99 | UNSAFE.copyMemory(values, FloatArrayUnsafeS.floatArrayOffs, null, addr, values.length << 2) 100 | 101 | } 102 | 103 | @inline def get(index: Int): Float = { 104 | 105 | UNSAFE.getFloat(addr + (index << 2)) 106 | 107 | } 108 | 109 | @inline def get(index: Long): Float = { 110 | 111 | UNSAFE.getFloat(addr + (index << 2)) 112 | 113 | } 114 | 115 | @inline def first: Float = { 116 | 117 | UNSAFE.getFloat(addr) 118 | 119 | } 120 | 121 | @inline def update(index: Int, value: Float): Unit = { 122 | 123 | UNSAFE.putFloat(addr + (index << 2), value) 124 | 125 | } 126 | 127 | @inline def update(index: Long, value: Float): Unit = { 128 | 129 | UNSAFE.putFloat(addr + (index << 2), value) 130 | 131 | } 132 | 133 | @inline final def incBy(index: Int, by: Float): Float = { 134 | 135 | val newValue = UNSAFE.getFloat(addr + (index << 2)) + by 136 | 137 | UNSAFE.putFloat(addr + (index << 2), newValue) 138 | 139 | newValue 140 | 141 | } 142 | 143 | @inline final def mulBy(index: Int, by: Float): Unit = { 144 | 145 | UNSAFE.putFloat(addr + (index << 2), UNSAFE.getFloat(addr + (index << 2)) * by) 146 | 147 | } 148 | 149 | @inline def toArray: Array[Float] = { 150 | 151 | val array = new Array[Float](sizev) 152 | 153 | UNSAFE.copyMemory(null, addr, array, FloatArrayUnsafeS.floatArrayOffs, sizev << 2) 154 | 155 | array 156 | 157 | } 158 | 159 | @inline def toArray(l: Int): Array[Float] = { 160 | 161 | val array = new Array[Float](l) 162 | 163 | UNSAFE.copyMemory(null, addr, array, FloatArrayUnsafeS.floatArrayOffs, l << 2) 164 | 165 | array 166 | 167 | } 168 | 169 | @inline def toArray(from: Int, until: Int): Array[Float] = { 170 | 171 | val array = new Array[Float](until - from) 172 | 173 | UNSAFE.copyMemory(null, addr + (from << 2), array, FloatArrayUnsafeS.floatArrayOffs, (until - from) << 2) 174 | 175 | array 176 | 177 | } 178 | 179 | override def toString: String = { 180 | 181 | val s: StringBuilder = new StringBuilder 182 | 183 | var i = 0 184 | 185 | while (i < sizev) { 186 | 187 | s.append(if (i < sizev - 1) 188 | get(i) + "," 189 | else 190 | get(i)) { 191 | 192 | i += 1 193 | 194 | i - 1 195 | 196 | } 197 | 198 | } 199 | 200 | s.toString 201 | 202 | } 203 | 204 | @inline def getAddr: Long = addr 205 | 206 | @inline def clone(padding: Int): FloatArrayUnsafeS = { 207 | 208 | val bau: FloatArrayUnsafeS = new FloatArrayUnsafeS(sizev + padding, aligned = aligned) 209 | 210 | UNSAFE.copyMemory(addr, bau.getAddr, sizev << 2) 211 | 212 | bau 213 | 214 | } 215 | 216 | } 217 | -------------------------------------------------------------------------------- /src/main/scala/utils/IntOrLongArrayUnsafe.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018-2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package utils 13 | 14 | @deprecated trait IntOrLongArrayUnsafe[@specialized T <: AnyVal] { // nope (cannot avoid boxing) 15 | 16 | @inline def size(): Int 17 | 18 | @inline def getAddr: Long 19 | 20 | @inline def setAddr(newAddr: Long): Unit 21 | 22 | @inline def toArray: Array[T] 23 | 24 | @inline def get(index: Int): T 25 | 26 | @inline def get(index: Long): T 27 | 28 | @inline def popLast: T 29 | 30 | @inline def update(index: Int, value: T): Unit 31 | 32 | @inline def removeByIndex(index: Int): Unit 33 | 34 | @inline def cloneToNew(padding: Int, keep: Int, cutOff: Boolean): IntOrLongArrayUnsafe[T] 35 | 36 | @inline def free(): Unit 37 | 38 | @inline def addToGarbage(): Unit 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/scala/utils/LongArrayUnsafeS.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018,2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package utils 13 | 14 | import input.UNSAFEhelper._ 15 | import it.unimi.dsi.fastutil.longs.{LongArrayList, LongOpenHashSet} 16 | 17 | 18 | /** This is not a general-purpose unsafe array class - designed for use in project diff-SAT only! */ 19 | class LongArrayUnsafeS(var sizev: Int) extends IntOrLongArrayUnsafe[Long] { 20 | 21 | //private[this] val unsafe: Unsafe = sharedDefs.unsafe 22 | 23 | private[this] val alignment = 0 24 | 25 | //private[this] val alignment = UNSAFE.pageSize // currently not used (see code below), no visible effect in diff-SAT 26 | 27 | //private[this] val internalPaddingFact = 0 28 | 29 | //private[this] val longArrayOffs = UNSAFE.arrayBaseOffset(classOf[Array[Long]]) 30 | 31 | //private[this] val aligned = false // must be false (true not fully implemented yet) 32 | 33 | private[this] var allocated = (sizev << 3) + alignment 34 | 35 | //sharedDefs.offHeapAllocatedEstimate += allocateSize 36 | 37 | private[this] var addr: Long = if (alignment == 0) { // without private[this] the field access in the bytecode would be by invokevirtual 38 | 39 | allocateOffHeapMem(allocated) 40 | 41 | } else { // the following only makes sense where alignment is different from unsafe's alignment (sizeOf?) 42 | 43 | var addra = allocateOffHeapMem(allocated) 44 | 45 | if (alignment > 0l && (addra & (alignment - 1l)) != 0) 46 | addra += (alignment - (addra & (alignment - 1))) 47 | 48 | addra // + alignment * internalPaddingFact 49 | 50 | } 51 | 52 | private[this] val addrMid: Long = addr + ((sizev >> 1) << 3) // nope: addr + (allocateSize / 2) 53 | 54 | assert((addrMid - addr) % 8 == 0) 55 | 56 | @inline def this(values: LongArrayList) { 57 | 58 | this(sizev = values.size) 59 | 60 | var i = 0 61 | 62 | while (i < sizev) { 63 | 64 | update(i, values.getLong((i))) 65 | 66 | i += 1 67 | 68 | } 69 | 70 | } 71 | 72 | @inline def this(s: Int, initValue: Long) { 73 | 74 | this(sizev = s) 75 | 76 | var i = 0 77 | 78 | while (i < s) { 79 | 80 | update(i, initValue) 81 | 82 | i += 1 83 | 84 | } 85 | 86 | } 87 | 88 | @inline def free(): Unit = { 89 | 90 | freeOffHeapMem(addr, allocated) 91 | 92 | 93 | } 94 | 95 | @inline def addToGarbage(): Unit = { 96 | 97 | addAllocOffHeapMemToGarbage(addr, allocated) 98 | 99 | } 100 | 101 | @inline def size(): Int = sizev 102 | 103 | @inline def compareAndUpdate(index: Int, expectedValue: Long, newValue: Long): Boolean = { 104 | 105 | UNSAFE.compareAndSwapLong(null, addr + (index << 3), expectedValue, newValue) 106 | 107 | } 108 | 109 | @inline def compareWithZeroAndUpdate(index: Int, newValue: Long): Boolean = { 110 | 111 | UNSAFE.compareAndSwapLong(null, addr + (index << 3), 0l, newValue) 112 | 113 | } 114 | 115 | @inline def get(index: Int): Long = { 116 | 117 | UNSAFE.getLong(addr + (index << 3)) 118 | 119 | } 120 | 121 | @inline def copyLongTo(fromIndex: Int, toIndex: Int): Unit = { 122 | 123 | UNSAFE.putLong(addr + (toIndex << 3), UNSAFE.getLong(addr + (fromIndex << 3))) 124 | 125 | } 126 | 127 | @inline def get(index: Long): Long = { 128 | 129 | UNSAFE.getLong(addr + (index << 3)) 130 | 131 | } 132 | 133 | @inline def getMid(index: Long): Long = { 134 | 135 | UNSAFE.getLong(addrMid + (index << 3)) 136 | 137 | } 138 | 139 | @inline def first: Long = { 140 | 141 | UNSAFE.getLong(addr) 142 | 143 | } 144 | 145 | @inline def last: Long = { 146 | 147 | UNSAFE.getLong(addr + ((sizev - 1) << 3)) 148 | 149 | } 150 | 151 | @inline def popLast: Long = { 152 | 153 | sizev -= 1 154 | 155 | UNSAFE.getLong(addr + (sizev << 3)) 156 | 157 | } 158 | 159 | @inline def update(index: Int, value: Long): Unit = { 160 | 161 | UNSAFE.putLong(addr + (index << 3), value) 162 | 163 | } 164 | 165 | @inline def updateVolatile(index: Int, value: Long): Unit = { 166 | 167 | UNSAFE.putLongVolatile(null, addr + (index << 3), value) 168 | 169 | } 170 | 171 | @inline def updateOrdered(index: Int, value: Long): Unit = { 172 | 173 | UNSAFE.putOrderedLong(null, addr + (index << 3), value) 174 | 175 | } 176 | 177 | @inline def updateMid(index: Int/*<- negative or positive*/, value: Long): Unit = { 178 | 179 | UNSAFE.putLong(addrMid + (index << 3), value) 180 | 181 | } 182 | 183 | @inline def update(index: Int, value: Int): Unit = { 184 | 185 | UNSAFE.putLong(addr + (index << 3), value) 186 | 187 | } 188 | 189 | @inline def update(index: Long, value: Long): Unit = { 190 | 191 | UNSAFE.putLong(addr + (index << 3), value) 192 | 193 | } 194 | 195 | @inline def inc(index: Int): Long = { 196 | 197 | UNSAFE.getAndAddLong(null, addr + (index << 3), 1) 198 | 199 | } 200 | 201 | @inline def dec(index: Int): Long = { 202 | 203 | UNSAFE.getAndAddLong(null, addr + (index << 3), -1) - 1 204 | 205 | } 206 | 207 | @inline def incBy(index: Int, x: Long): Unit = { 208 | 209 | UNSAFE.getAndAddLong(null, addr + (index << 3), x) 210 | 211 | } 212 | 213 | @inline def compareAndSwap(index: Int, valueOld: Long, valueNew: Long): Unit = { 214 | 215 | UNSAFE.compareAndSwapLong(null, addr + (index << 3), valueOld, valueNew) 216 | 217 | } 218 | 219 | @inline def removeByIndex(index: Int): Unit = { 220 | 221 | sizev -= 1 222 | 223 | if(index < sizev) { 224 | 225 | update(index, get(sizev)) 226 | 227 | } 228 | 229 | } 230 | 231 | @inline def filter(keepBy: Long => Boolean): Unit = { 232 | 233 | var k = sizev - 1 234 | 235 | while(k >= 0) { 236 | 237 | if(!keepBy(get(k))) 238 | removeByIndex(k) 239 | 240 | k -= 1 241 | 242 | } 243 | 244 | } 245 | 246 | 247 | @inline def toArray(): Array[Long] = { 248 | 249 | val array = new Array[Long](sizev) 250 | 251 | UNSAFE.copyMemory(null, addr, array, UNSAFE.arrayBaseOffset(classOf[Array[Long]]), sizev << 3) 252 | 253 | array 254 | 255 | } 256 | 257 | @inline def toArray(l: Int): Array[Long] = { 258 | 259 | val array = new Array[Long](l) 260 | 261 | UNSAFE.copyMemory(null, addr, array, UNSAFE.arrayBaseOffset(classOf[Array[Long]]), l << 3) 262 | 263 | array 264 | 265 | } 266 | 267 | override def toString: String = { 268 | 269 | val s: StringBuilder = new StringBuilder 270 | 271 | var i = 0 272 | 273 | while (i < sizev) { 274 | 275 | s.append(if (i < sizev - 1) 276 | get(i) + "," 277 | else 278 | get(i)) { 279 | 280 | i += 1 281 | 282 | i - 1 283 | 284 | } 285 | 286 | } 287 | 288 | s.toString 289 | 290 | } 291 | 292 | @inline def getAddr: Long = addr 293 | 294 | @inline def setAddr(newAddr: Long): Unit = addr = newAddr 295 | 296 | @inline def getAllocated: Int = allocated 297 | 298 | @inline def clone(padding: Int): LongArrayUnsafeS = { 299 | 300 | val bau: LongArrayUnsafeS = new LongArrayUnsafeS(sizev + padding) 301 | 302 | UNSAFE.copyMemory(addr, bau.getAddr, sizev << 3) 303 | 304 | bau 305 | 306 | } 307 | 308 | @inline def cloneToNew(padding: Int, keep: Int, cutOff: Boolean): LongArrayUnsafeS = { 309 | 310 | val bau: LongArrayUnsafeS = new LongArrayUnsafeS(if(cutOff) keep + padding else sizev + padding) 311 | 312 | //assert(addr + ((sizev - 1) << 3) < bau.getAddr || bau.getAddr + ((sizev + padding - 1) << 3) < addr) 313 | 314 | UNSAFE.copyMemory(addr, bau.getAddr, /*sizev*/keep << 3) 315 | 316 | bau 317 | 318 | } 319 | 320 | @inline def resize(newSize: Int): Unit = { // see resize(newSize: Int, retain: Int) for the case where only a part of the current content needs to be retained 321 | 322 | allocated = newSize << 3 323 | 324 | addr = resizeOffHeapMem(addr, sizev << 3, allocated) 325 | 326 | sizev = newSize 327 | 328 | } 329 | 330 | @inline def resize(newSize: Int, retain: Int): Unit = { 331 | 332 | allocated = newSize << 3 333 | 334 | val newAddr = allocateOffHeapMem(allocated) 335 | 336 | if(retain > 0) 337 | UNSAFE.copyMemory(addr, newAddr, retain << 3) 338 | 339 | freeOffHeapMem(addr, sizev << 3) 340 | 341 | sizev = newSize 342 | 343 | addr = newAddr 344 | 345 | } 346 | 347 | def sortByInplace(by: Long => Double, until: Int): Unit = { 348 | 349 | // insertion sort - use only if array is very small (but then it's fast): 350 | 351 | var j = 1 352 | 353 | var key = -1l 354 | 355 | var i = -1 356 | 357 | while (j < until) { 358 | 359 | key = get(j) 360 | 361 | i = j - 1 362 | 363 | while (i > -1 && by(get(i)) > by(key)) { 364 | 365 | update(i + 1, get(i)) 366 | 367 | i -= 1 368 | 369 | } 370 | 371 | update(i + 1, key) 372 | 373 | j += 1 374 | 375 | } 376 | 377 | } 378 | 379 | @inline def swap(i: Int, j: Int) = { 380 | 381 | val h = get(i) 382 | 383 | update(i, get(j)) 384 | 385 | update(j, h) 386 | 387 | } 388 | 389 | // Returns index of k-th smallest item in this array 390 | def floydRivest(leftR: Int, rightR: Int, k: Int, by: Long => Double): Int = { 391 | 392 | var left = leftR 393 | 394 | var right = rightR 395 | 396 | while (right > left) { 397 | 398 | if (right - left > 600) { // sample subarray (constants 600, 0.5 largely arbitrary, from original implementation) 399 | 400 | val n = right - left + 1 401 | 402 | val i = k - left + 1 403 | 404 | val z = Math.log(n) 405 | 406 | val s = 0.5 * Math.exp(2 * z / 3) 407 | 408 | val sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * Math.signum(i - n / 2) 409 | 410 | val newLeft = Math.max(left, (k - i * s / n + sd).toInt) 411 | 412 | val newRight = Math.min(right, (k + (n - i) * s / n + sd).toInt) 413 | 414 | floydRivest(newLeft, newRight, k, by) // TODO: the Ints get boxed 415 | 416 | } 417 | 418 | val t = get(k) 419 | 420 | var i = left 421 | 422 | var j = right 423 | 424 | swap(left, k) 425 | 426 | if (by(get(right)) > by(t)) 427 | swap(left, right) 428 | 429 | while (i < j) { 430 | 431 | swap(i, j) 432 | 433 | i += 1 434 | 435 | j -= 1 436 | 437 | while (by(get(i)) < by(t)) 438 | i += 1 439 | 440 | while (by(get(j)) > by(t)) 441 | j -= 1 442 | 443 | } 444 | 445 | if (by(get(left)) == by(t)) 446 | swap(left, j) 447 | else { 448 | 449 | j += 1 450 | 451 | swap(right, j) 452 | 453 | } 454 | 455 | if (j <= k) 456 | left = j + 1 457 | 458 | if (k <= j) 459 | right = j - 1 460 | 461 | } 462 | 463 | k //get(k) 464 | 465 | } 466 | 467 | @inline def removeDuplicates(to: Int): Unit = { 468 | 469 | val hashForDuplRem = new LongOpenHashSet() 470 | 471 | var src = 0 472 | 473 | var dest = 0 474 | 475 | while(src <= to) { 476 | 477 | if(src == 0 || !hashForDuplRem.contains(get(src))) { 478 | 479 | hashForDuplRem.add(get(src)) 480 | 481 | update(dest, get(src)) 482 | 483 | dest += 1 484 | 485 | } 486 | 487 | src += 1 488 | 489 | } 490 | 491 | sizev = dest 492 | 493 | } 494 | 495 | @inline def establishDuplicateFreePrefixBy(by: Long => Long, to: Int): Int = { 496 | 497 | val hashForDuplRem = new LongOpenHashSet() 498 | 499 | var src = 0 500 | 501 | var dest = 0 502 | 503 | while(src <= to) { 504 | 505 | if(src == 0 || !hashForDuplRem.contains(by(get(src)))) { 506 | 507 | hashForDuplRem.add(by(get(src))) 508 | 509 | update(dest, get(src)) 510 | 511 | dest += 1 512 | 513 | } 514 | 515 | src += 1 516 | 517 | } 518 | 519 | dest 520 | 521 | } 522 | 523 | } 524 | -------------------------------------------------------------------------------- /src/main/scala/utils/RandomLongSet.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import java.util.Random 4 | 5 | import it.unimi.dsi.fastutil.longs.{Long2IntOpenHashMap, LongArrayList} 6 | 7 | /** 8 | * For positive (Scala-)Longs only (in case of diff-SAT: nogood reducibles). Not suitable for cryptographic purposes. 9 | */ 10 | class RandomLongSet(expectedNoOfItems: Int = 1024) { 11 | 12 | val data = new LongArrayList(expectedNoOfItems) 13 | 14 | val index = new Long2IntOpenHashMap(expectedNoOfItems) 15 | // ^ maps items to their indices in data 16 | 17 | index.defaultReturnValue(-1) // important because otherwise the "not found" value would be 0 which is a valid item in our use case 18 | 19 | @inline def clear(): Unit = { 20 | 21 | index.clear() 22 | 23 | data.clear() 24 | 25 | } 26 | 27 | @inline def contains(item: Long): Boolean = { 28 | 29 | index.containsKey(item) 30 | 31 | } 32 | 33 | @inline def add(item: Long): Unit = { // NB: in contrast to similar standard lib methods, we avoid returning (boxed) Boolean success indicators 34 | 35 | if (!index.containsKey(item)) { 36 | 37 | index.put(item, data.size) 38 | 39 | data.add(item) 40 | 41 | } 42 | 43 | } 44 | 45 | @inline def removeAt(id: Int): Unit = { 46 | 47 | if (id < data.size) { 48 | 49 | index.remove(data.getLong(id)) 50 | 51 | val last: Long = data.removeLong(data.size - 1) 52 | 53 | if (id < data.size) { 54 | 55 | index.put(last, id) 56 | 57 | data.set(id, last) 58 | 59 | } 60 | 61 | } 62 | 63 | } 64 | 65 | @inline def size: Int = data.size 66 | 67 | @inline def remove(item: Long): Unit = { 68 | 69 | val id = index.get(item) 70 | 71 | if (id >= 0) 72 | removeAt(id) 73 | 74 | } 75 | 76 | @inline def get(id: Int): Long = data.getLong(id) 77 | 78 | @inline def getRandomItem(rnd: Random): Long = { 79 | 80 | val id = rnd.nextInt(data.size) 81 | 82 | data.getLong(id) 83 | 84 | } 85 | 86 | @inline def pollRandomItem(rnd: Random): Long = { 87 | 88 | val id: Int = rnd.nextInt(data.size) 89 | 90 | val res: Long = data.getLong(id) 91 | 92 | removeAt(id) 93 | 94 | res 95 | 96 | } 97 | 98 | 99 | } -------------------------------------------------------------------------------- /src/main/scala/utils/SatalyzerUse.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018, 2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package utils 13 | 14 | import java.io.{BufferedOutputStream, File, FileOutputStream, OutputStream} 15 | import java.time.Instant 16 | import java.util 17 | import java.util.UUID 18 | 19 | import com.jsoniter.output.JsonStream 20 | 21 | import input.diffSAT 22 | import input.diffSAT.stats 23 | 24 | import sharedDefs._ 25 | import utils.Various._ 26 | 27 | 28 | // For visualization of the serialized runtime statistics events, use Satalyzer (separate project) 29 | 30 | final case class StatsEntry( // Any modification of this class (e.g., new fields or field names) need to be reflected in the 31 | // deserialization method further below (can't be don't automatically). 32 | // Also, of course, class StatsEntry needs to be identical with StatsEntry in project RuntimeStats/StatsVisualizer. 33 | 34 | messageTimeStamp: Long = -1l, // in nano secs from program start (not to be used as a unique entry or message key) 35 | 36 | threadNo: Int = 0, 37 | 38 | key: String = null, 39 | 40 | valueStr: String = null, 41 | 42 | @transient runIDTransient: String = null 43 | 44 | ) { 45 | 46 | } 47 | 48 | final case class Stats( // Any modification of this class (e.g., new fields or field names) need to be reflected in the 49 | // deserialization method further below (can't be don't automatically). 50 | // Also, of course, class Stats needs to be identical with Stats in project RuntimeStats/StatsVisualizer. 51 | 52 | problemFile: String, 53 | 54 | runID: String = UUID.randomUUID().toString, // a type-4 UUID represented as a string 55 | 56 | runStartTimeMs: Long = Instant.now.toEpochMilli(), 57 | 58 | // any other information, including solving duration, should be written as StatsEntry entries 59 | 60 | entries: util.List[StatsEntry] /*doesn't serialize properly: ArrayBuffer[StatsEntry]*/ = 61 | new util.ArrayList[StatsEntry](), 62 | 63 | @transient var outFile: File = null, 64 | 65 | @transient var statsFileStream: OutputStream = null, 66 | 67 | @transient var lastFlush: Long = 0l 68 | 69 | ) { 70 | 71 | def initializeStatsFile(): Unit = { 72 | 73 | val outFileName = "stats_" + toString.replaceAll("[^\\w.-]", "_") + "(UTC).json" 74 | 75 | val dir = new File(writeStatsDirectory) 76 | 77 | outFile = new File(dir, outFileName) 78 | 79 | } 80 | 81 | @inline def initializeStatsFileStream(): Unit = { 82 | 83 | assert(outFile != null) 84 | 85 | statsFileStream = new BufferedOutputStream( 86 | new FileOutputStream(outFile)) 87 | 88 | } 89 | 90 | /** This writes the entire stats to file, replacing any existing file with the respective name */ 91 | @inline def writeToFile(): Unit = { 92 | 93 | if (writeRuntimeStatsToFile) { // for performance reasons, this condition should ideally be checked already 94 | // before calling this method 95 | 96 | initializeStatsFileStream() 97 | 98 | try { 99 | 100 | JsonStream.serialize(stats, statsFileStream) // closes stream! 101 | 102 | } catch { // TODO: probable reason: Graal 20 native image not working with Jsoniter 103 | 104 | case e: Exception => diffSAT.stomp(-10000, "Cannot serialize runtime stats: " + e + "\nIf you are using a native image executable, it is recommended to try writeRuntimeStatsToFile() with a JVM instead") 105 | 106 | } 107 | 108 | } 109 | 110 | } 111 | 112 | /** Don't use this for frequent writing such as logging. For long period statistics gathering only. 113 | */ 114 | @inline def writeEntry(key: String, value: scala.Any, 115 | solverThreadNo: Int = 0 /*0: outside SAT solver thread*/ , 116 | replace: Boolean = false): Unit = { 117 | 118 | if (writeRuntimeStatsToFile) { // for performance reasons, this condition should ideally be checked already 119 | // before calling writeEntry 120 | 121 | val messageTimeStampRelativeToProgStartNs = (Instant.now.toEpochMilli() - runStartTimeMs) * 1000000l // TODO 122 | 123 | val valueStr = value.toString() 124 | 125 | val newStatsEntry = new StatsEntry(messageTimeStamp = messageTimeStampRelativeToProgStartNs, 126 | threadNo = solverThreadNo, key = key, valueStr = valueStr, 127 | runIDTransient = runID) 128 | 129 | //println(statsEntry) 130 | 131 | this.synchronized { 132 | 133 | assert(newStatsEntry != null) 134 | 135 | if (replace) { // (costly, but rarely used and for deserialization-related reasons we shouldn't use a map here anyway) 136 | 137 | var i = stats.entries.size() - 1 138 | 139 | while (i >= 0) { 140 | 141 | val existingEntry = stats.entries.get(i) 142 | 143 | if (existingEntry.key == key) { 144 | 145 | stats.entries.set(i, newStatsEntry) 146 | 147 | i = -2 148 | 149 | } else 150 | i -= 1 151 | 152 | } 153 | 154 | if (i == -2) 155 | writeToFile() // since otherwise any existing serialization would be "outdated" (containing the old value of the unique key) 156 | else 157 | entries.add(newStatsEntry) 158 | 159 | } else 160 | entries.add(newStatsEntry) 161 | 162 | val currentTimerNs = System.nanoTime() 163 | 164 | if (currentTimerNs - lastFlush > flushRuntimeStatsEverySec /*sec*/ * 1000000000l) { 165 | 166 | lastFlush = currentTimerNs 167 | 168 | writeToFile() 169 | 170 | } 171 | 172 | } 173 | 174 | } 175 | 176 | } 177 | 178 | def getFirstOpt(key: String): Option[StatsEntry] = { 179 | 180 | var i = 0 181 | 182 | while (i < entries.size()) { 183 | 184 | val existingEntry = entries.get(i) 185 | 186 | if (existingEntry.key == key) 187 | return Some(existingEntry) 188 | 189 | i += 1 190 | 191 | } 192 | 193 | None 194 | 195 | } 196 | 197 | def countEntriesWithKey(key: String, maxCount: Int = Int.MaxValue): Int = { 198 | 199 | var count = 0 200 | 201 | var i = 0 202 | 203 | while (i < entries.size()) { 204 | 205 | if (entries.get(i).key == key) { 206 | 207 | count += 1 208 | 209 | if (count >= maxCount) 210 | return count 211 | 212 | } 213 | 214 | i += 1 215 | 216 | } 217 | 218 | count 219 | 220 | } 221 | 222 | override def toString(): String = { 223 | 224 | val runStartTimeMsStr = Instant.ofEpochMilli(runStartTimeMs).toString 225 | 226 | val problemName = fileNameFromFullPath(problemFile) 227 | 228 | problemName + " [" + runStartTimeMsStr + "]" 229 | 230 | } 231 | 232 | } 233 | 234 | -------------------------------------------------------------------------------- /src/main/scala/utils/Tarjan.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018,2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package utils 13 | 14 | import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap 15 | 16 | import scala.collection.mutable 17 | 18 | /** 19 | * Not a general-purpose Tarjan implementation. Not well-tested yet. 20 | */ 21 | object Tarjan { 22 | 23 | /** Based on code from https://stackoverflow.com/a/18290478, somewhat amended */ 24 | def trajanRec(g: Int2ObjectOpenHashMap[List[Int]]): mutable.ArrayBuffer[mutable.ArrayBuffer[Int]] = { 25 | 26 | // TODO: create iterative variant 27 | 28 | val s = mutable.Buffer.empty[Int] 29 | 30 | val sSet = mutable.Set.empty[Int] 31 | 32 | val index = new java.util.HashMap[Int, Int]() 33 | 34 | val lowLink = new java.util.HashMap[Int, Int]() 35 | 36 | val ret = mutable.ArrayBuffer.empty[mutable.ArrayBuffer[Int]] 37 | 38 | def visit(v: Int): Unit = { 39 | 40 | index.put(v, index.size) 41 | 42 | lowLink.put(v, index.get(v)) 43 | 44 | s += v 45 | 46 | sSet += v 47 | 48 | for(w <- g.get(v)) { 49 | 50 | if(!index.keySet.contains(w)) { 51 | 52 | visit(w) 53 | 54 | lowLink.put(v, math.min(lowLink.get(w), lowLink.get(v))) 55 | 56 | } else if(sSet(w)) 57 | lowLink.put(v, math.min(lowLink.get(v), index.get(w))) 58 | } 59 | 60 | if(lowLink.get(v) == index.get(v)) { 61 | 62 | val scc = mutable.ArrayBuffer.empty[Int] 63 | 64 | var w = Int.MinValue // -1 65 | 66 | while(v != w) { 67 | 68 | w = s.remove(s.size - 1) 69 | 70 | scc += w 71 | 72 | sSet -= w 73 | 74 | } 75 | 76 | ret += scc 77 | 78 | } 79 | } 80 | 81 | val gKeysIterator = g.keySet().iterator() 82 | 83 | while(gKeysIterator.hasNext()) { 84 | 85 | val v = gKeysIterator.nextInt() 86 | 87 | if (/*v >= 0 &&*/ !index.keySet.contains(v)) 88 | visit(v) 89 | 90 | } 91 | 92 | ret 93 | 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/scala/utils/Various.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * diff-SAT 3 | * 4 | * Copyright (c) 2018, 2020 Matthias Nickles 5 | * 6 | * matthiasDOTnicklesATgmxDOTnet 7 | * 8 | * This code is licensed under MIT License (see file LICENSE for details) 9 | * 10 | */ 11 | 12 | package utils 13 | 14 | import input.diffSAT 15 | import input.diffSAT.changeConsoleWidthUnderWin 16 | import sharedDefs.RandomGenSuperclass 17 | 18 | import scala.collection.mutable 19 | 20 | object Various { 21 | 22 | /** Plain Fisher-Yates shuffle on init of array */ 23 | @inline def shuffleArray[A](array: mutable.Seq[A], rg: RandomGenSuperclass, to: Int = -1): Unit = { 24 | 25 | if (to < 0 && array.length >= 16384) 26 | shuffleArrayBlocked[A](array, rg) 27 | else { 28 | 29 | val l = if (to < 0) array.length - 1 else to 30 | 31 | var i = l 32 | 33 | while (i > 0) { 34 | 35 | val j = rg.nextInt(i + 1) 36 | 37 | val temp = array(j) 38 | 39 | array(j) = array(i) 40 | 41 | array(i) = temp 42 | 43 | i -= 1 44 | 45 | } 46 | 47 | } 48 | 49 | } 50 | 51 | /** Fisher-Yates-Durstenfeld shuffle */ 52 | @inline def shuffleArrayUnsafe(array: IntArrayUnsafeS, rg: RandomGenSuperclass, from /*inclusive*/ : Long = 0l, to /*inclusive*/ : Long = -1l): Unit = { 53 | 54 | val l = if (to < 0l) array.sizev - 1 else to 55 | 56 | var i: Long = l 57 | 58 | while (i > from) { 59 | 60 | val j = rg.nextInt(i.toInt + 1) 61 | 62 | val temp = array.get(j) 63 | 64 | array.update(j, array.get(i)) 65 | 66 | array.update(i, temp) 67 | 68 | i -= 1 69 | 70 | } 71 | 72 | } 73 | 74 | /** Fisher-Yates-Durstenfeld shuffle */ 75 | @inline def shuffleLongArrayUnsafe(array: LongArrayUnsafeS, rg: RandomGenSuperclass, from /*inclusive*/ : Long = 0l, to /*inclusive*/ : Long = -1l): Unit = { 76 | 77 | val l = if (to < 0l) array.sizev - 1 else to 78 | 79 | var i: Long = l 80 | 81 | while (i > from) { 82 | 83 | val j = rg.nextInt(i.toInt + 1) 84 | 85 | val temp = array.get(j) 86 | 87 | array.update(j, array.get(i)) 88 | 89 | array.update(i, temp) 90 | 91 | i -= 1 92 | 93 | } 94 | 95 | } 96 | 97 | /** Blocked Fisher-Yates shuffle */ 98 | @inline def shuffleArrayBlocked[A](arr: mutable.Seq[A], rg: RandomGenSuperclass): Unit = { 99 | // (method code based on public domain code from https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog) 100 | 101 | def swap(i: Int, j: Int) = { 102 | 103 | val tmp = arr(i) 104 | 105 | arr(i) = arr(j) 106 | 107 | arr(j) = tmp 108 | 109 | } 110 | 111 | val size = arr.length 112 | 113 | val block = 1024 114 | 115 | val buffer = Array.ofDim[Int](block) 116 | 117 | var i = size 118 | 119 | while (i > block + 1) { 120 | 121 | var k = 0 122 | 123 | while (k < block) { 124 | 125 | buffer(k) = rg.nextInt(i - k) 126 | 127 | k += 1 128 | 129 | } 130 | 131 | k = 0 132 | 133 | while (k < block) { 134 | 135 | swap(i - 1 - k, buffer(k)) 136 | 137 | k += 1 138 | 139 | } 140 | 141 | i -= block 142 | 143 | } 144 | 145 | while (i > 1) { 146 | 147 | swap(i - 1, rg.nextInt(i)) 148 | 149 | i -= 1 150 | 151 | } 152 | 153 | } 154 | 155 | @inline def round(n: Double, digits: Int): Double = { 156 | 157 | val p = Math.pow(10d, digits) 158 | 159 | Math.round(n * p) / p 160 | 161 | } 162 | 163 | 164 | @inline def powFloat(a: Float, b: Float): Float = { 165 | 166 | Math.pow(a, b).toFloat //org.apache.commons.math3.util.FastMath.pow(a, b).toFloat 167 | 168 | } 169 | 170 | @inline def powInt(a: Double, b: Int): Double = org.apache.commons.math3.util.FastMath.pow(a, b) 171 | 172 | @inline def toK(n: Long): String = n / 1000L + "K" 173 | 174 | @inline def toM(n: Long): String = round(n.toDouble / 1000000d, 1) + "M" 175 | 176 | @inline def toB(n: Long): String = (n / 1000000000) + "B" 177 | 178 | @inline def toKorM(n: Long): String = if (n < 1000) n.toString else if (n < 1000000) toK(n) else if(n < 1000000000) toM(n) else toB(n) 179 | 180 | @inline def printStatusLine(pStrR: String, cutOffAt: Int = 0) = { 181 | 182 | /* println("sharedDefs.maxAssumedConsoleWidth = " + sharedDefs.maxAssumedConsoleWidth) 183 | 184 | println("diffSAT.osWin = " + diffSAT.osWin) 185 | 186 | println("changeConsoleWidthUnderWin = " + changeConsoleWidthUnderWin) 187 | */ 188 | val pStr = if(sharedDefs.debug) pStrR else pStrR.take(sharedDefs.maxAssumedConsoleWidth.max(cutOffAt))+(" " * (sharedDefs.maxAssumedConsoleWidth.max(cutOffAt) - pStrR.length).max(0)) // progress line scrolls if larger than console width. No reliable way to determine console width in Java 189 | 190 | if(diffSAT.osWin /*&& changeConsoleWidthUnderWin*/) { // unclear if this needs to be done by all versions of Windows and all builds of Windows 10: 191 | 192 | // System.out.write(pStr.getBytes()) // if more than 4 lines, IntelliJ console flickers 193 | 194 | // \r moves to start of current console row (not supported with all OS) 195 | 196 | System.out.print(("\b" * 1100) + "\r" + pStr + " ") 197 | // with Win, \b's seem to required in addition to '\r'. But still doesn't work if console line buffer width to small. 198 | 199 | // print("\u001b[s" + pStr + "\u001b[u") // nope in Win 200 | 201 | } else { 202 | 203 | // System.out.print("\u001b[1A\u001b[2K") 204 | 205 | System.out.write(("\r" + pStr).getBytes()) 206 | 207 | } 208 | 209 | System.out.flush() 210 | 211 | } 212 | 213 | @inline def timerToElapsedMs(startNano: Long): Long = (System.nanoTime() - startNano) / 1000000 214 | 215 | @inline def next2Pow(x: Int): Int = if (x <= 1) 1 else 1 << (32 - Integer.numberOfLeadingZeros(x - 1)) 216 | 217 | /** Divisor must be a power of 2 */ 218 | @inline def fastModByPow2(dividend: Int, divisor: Int): Int = dividend & (divisor - 1) 219 | 220 | @inline def binLog(x: Int): Int = { 221 | 222 | //assert(x != 0) 223 | 224 | 31 - Integer.numberOfLeadingZeros(x) 225 | 226 | } 227 | 228 | 229 | def fileNameFromFullPath(fileStr: String): String = { 230 | 231 | java.nio.file.Paths.get(fileStr).getFileName().toString() 232 | 233 | } 234 | 235 | /** 236 | * Pretty prints a Scala value similar to its source represention. 237 | * Particularly useful for case classes. 238 | * Derived from code at https://gist.github.com/carymrobbins/7b8ed52cd6ea186dbdf8 239 | * with enhancements: @see https://gist.github.com/myDisconnect/1f7046b23e18b4b43dd3c5932d0db7dc 240 | * 241 | * @param a - The value to pretty print. 242 | * @param indentSize - Number of spaces for each indent. 243 | * @param maxElementWidth - Largest element size before wrapping. 244 | * @param depth - Initial depth to pretty print indents. 245 | * @return 246 | */ 247 | def prettyPrint(a: Any, indentSize: Int = 3, maxElementWidth: Int = 30, depth: Int = 0, omit: List[String]): String = { 248 | 249 | val indent = " " * depth * indentSize 250 | 251 | val fieldIndent = indent + (" " * indentSize) 252 | 253 | val nextDepth = prettyPrint(_: Any, indentSize, maxElementWidth, depth + 1, omit = omit) 254 | 255 | a match { 256 | 257 | case s: String => 258 | val replaceMap = Seq( 259 | "\n" -> "\\n", 260 | "\r" -> "\\r", 261 | "\t" -> "\\t", 262 | "\"" -> "\\\"" 263 | ) 264 | '"' + replaceMap.foldLeft(s) { case (acc, (c, r)) => acc.replace(c, r) } + '"' 265 | 266 | case opt: Some[_] => 267 | val resultOneLine = s"Some(${nextDepth(opt.get)})" 268 | if (resultOneLine.length <= maxElementWidth) return resultOneLine 269 | s"Some(\n$fieldIndent${nextDepth(opt.get)}\n$indent)" 270 | 271 | case xs: Seq[_] if xs.isEmpty => 272 | xs.toString() 273 | 274 | case map: Map[_, _] if map.isEmpty => 275 | map.toString() 276 | 277 | case xs: Map[_, _] => 278 | val result = xs.map { case (key, value) => s"\n$fieldIndent${nextDepth(key)} -> ${nextDepth(value)}" }.toString 279 | "Map" + s"${result.substring(0, result.length - 1)}\n$indent)".substring(4) 280 | 281 | // Make Strings look similar to their literal form. 282 | // For an empty Seq just use its normal String representation. 283 | case xs: Seq[_] => 284 | // If the Seq is not too long, pretty print on one line. 285 | val resultOneLine = xs.map(nextDepth).toString() 286 | if (resultOneLine.length <= maxElementWidth) return resultOneLine 287 | // Otherwise, build it with newlines and proper field indents. 288 | val result = xs.map(x => s"\n$fieldIndent${nextDepth(x)}").toString() 289 | result.substring(0, result.length - 1) + "\n" + indent + ")" 290 | 291 | // Product should cover case classes. 292 | case p: Product => 293 | val prefix = p.productPrefix 294 | // We'll use reflection to get the constructor arg names and values. 295 | val cls = p.getClass 296 | val fields = cls.getDeclaredFields.filterNot(_.isSynthetic).map(_.getName) 297 | val values = p.productIterator.toSeq 298 | // If we weren't able to match up fields/values, fall back to toString. 299 | if (fields.length != values.length) return p.toString 300 | fields.zip(values).toList match { 301 | // If there are no fields, just use the normal String representation. 302 | case Nil => p.toString 303 | // If there is more than one field, build up the field names and values. 304 | case kvps => 305 | val prettyFields = kvps.map { 306 | 307 | case (k, v) => { 308 | 309 | if (omit.contains(k)) 310 | s"$fieldIndent$k = (omitted)" 311 | else 312 | s"$k = ${nextDepth(v)}" 313 | 314 | } 315 | 316 | 317 | } 318 | // If the result is not too long, pretty print on one line. 319 | val resultOneLine = s"$prefix(${prettyFields.mkString(", ")})" 320 | if (resultOneLine.length <= maxElementWidth) return resultOneLine 321 | // Otherwise, build it with newlines and proper field indents. 322 | s"$prefix(\n${ 323 | kvps.map { 324 | 325 | // case ("dependencyGraph", _) => fieldIndent + "dependencyGraph = (omitted)" 326 | 327 | case (k, v) => { 328 | 329 | if (omit.contains(k)) 330 | s"$fieldIndent$k = (omitted)" 331 | else 332 | s"$fieldIndent$k = ${nextDepth(v)}" 333 | 334 | 335 | } 336 | 337 | 338 | }.mkString(",\n") 339 | }\n$indent)" 340 | } 341 | 342 | // if we haven't specialized this type, just use its toString. 343 | case _ => a.toString 344 | 345 | } 346 | 347 | } 348 | 349 | } 350 | -------------------------------------------------------------------------------- /src/main/scala/utils/XORShift32.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | import java.util.Optional; 4 | 5 | /** 6 | * Medium-quality random numbers using a basic XOR-shift algorithm. For underlying basic algorithm see, e.g., http://www.javamex.com/tutorials/random_numbers/xorshift.shtml 7 | * Not suitable as a source of randomness for cryptography. Not thread-safe. 8 | *

9 | * For algo with better quality (longer period) but also not cryptographically secure, consider using a >=128 XOR-Shift, 10 | * see, e.g., https://www.codeproject.com/Articles/9187/A-fast-equivalent-for-System-Random 11 | */ 12 | 13 | public class XORShift32 extends java.util.Random { 14 | 15 | private long x; 16 | 17 | public XORShift32() { 18 | 19 | this(Optional.empty()); 20 | 21 | } 22 | 23 | public XORShift32(Optional seedOpt) { 24 | 25 | x = seedOpt.orElse(System.nanoTime()); 26 | 27 | } 28 | 29 | @Override 30 | public long nextLong() { 31 | 32 | x ^= (x << 21); 33 | 34 | x ^= (x >>> 35); 35 | 36 | x ^= (x << 4); 37 | 38 | return x; 39 | 40 | } 41 | 42 | public long nextPosLong() { 43 | 44 | return nextLong() & 0x7fffffffffffffffL; 45 | 46 | } 47 | 48 | @Override 49 | public int nextInt() { 50 | 51 | return (int) nextLong(); 52 | 53 | } 54 | 55 | public int nextPosInt() { 56 | 57 | return nextInt() & 0x7fffffff; 58 | 59 | } 60 | 61 | @Override 62 | public int nextInt(int max) { 63 | 64 | return (int) (nextPosLong() / (0x7fffffffffffffffL / max + 1l)); 65 | 66 | /* somewhat better quality? (better at avoiding bias in lower bits): 67 | 68 | int threshold = (0x7fffffff - max + 1) % max; 69 | 70 | for (;;) { 71 | 72 | int bits = (int) (nextLong() & 0x7fffffff); 73 | 74 | if (bits >= threshold) 75 | return bits % max; 76 | 77 | } */ 78 | 79 | } 80 | 81 | @Override 82 | public float nextFloat() { 83 | 84 | return Math.scalb((float) (nextLong() & 0x7fffffffL), -31); 85 | 86 | } 87 | 88 | @Override 89 | public double nextDouble() { 90 | 91 | //return Math.scalb(nextLong() >>> 1, -63); 92 | return (nextLong() >>> (64 - 53)) * 0x1.0p-53; 93 | 94 | } 95 | 96 | @Override 97 | public boolean nextBoolean() { 98 | 99 | return nextLong() >= 0l; 100 | 101 | } 102 | 103 | /*public double nextTriangular(double a, double b, double c) { 104 | 105 | double d = (c - a) / (b - a); 106 | 107 | double rand = nextDouble(); 108 | 109 | if (rand < d) 110 | return a + Math.sqrt(rand * (b - a) * (c - a)); 111 | else 112 | return b - Math.sqrt((1 - rand) * (b - a) * (b - c)); 113 | 114 | }*/ 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/main/scala/utils/XoRoRNGd.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /* Based on code written in 2016 by David Blackman and Sebastiano Vigna (vigna@acm.org) 4 | http://squidpony.github.io/SquidLib/squidlib-util/apidocs/src-html/squidpony/squidmath/XoRoRNG.html 5 | */ 6 | 7 | /** 8 | * A port of Blackman and Vigna's xoroshiro128+ generator; should be very fast and produce medium-quality output. 9 | * Testing shows it is within 5% the speed of LightRNG, sometimes faster and sometimes slower, and has a larger period. 10 | * It's called XoRo because it involves Xor as well as Rotate operations on the 128-bit pseudo-random state. Note that 11 | * xoroshiro128+ fails some statistical quality tests systematically, and fails others often; if this could be a concern 12 | * for you, {@link DiverRNG}, which is the default for {@link RNG}, will be faster and won't fail tests, and 13 | * though its period is shorter, it would still take years to exhaust on one core generating only random numbers. 14 | *
15 | * {@link LightRNG} is also very fast, but relative to XoRoRNG it has a significantly shorter period (the amount of 16 | * random numbers it will go through before repeating), at {@code pow(2, 64)} as opposed to XorRNG and XoRoRNG's 17 | * {@code pow(2, 128) - 1}, but LightRNG also allows the current RNG state to be retrieved and altered with 18 | * {@code getState()} and {@code setState()}. For most cases, you should decide between DiverRNG, LightRNG, XoRoRNG, 19 | * and other RandomnessSource implementations based on your needs for period length and state manipulation (DiverRNG 20 | * is also used internally by almost all StatefulRNG objects). You might want significantly less predictable random 21 | * results, which {@link IsaacRNG} can provide, along with a large period. You may want a very long period of random 22 | * numbers, which would suggest {@link LongPeriodRNG} as a good choice or {@link MersenneTwister} as a potential 23 | * alternative. You may want better performance on 32-bit machines or on GWT, where {@link Starfish32RNG} is currently 24 | * the best choice most of the time, and {@link Lathe32RNG} can be faster but has slightly worse quality (both of these 25 | * generators use a 32-bit variant on the xoroshiro algorithm but change the output scrambler). These all can generate 26 | * pseudo-random numbers in a handful of nanoseconds (with the key exception of 64-bit generators being used on GWT, 27 | * where they may take more than 100 nanoseconds per number), so unless you need a LOT of random numbers in a hurry, 28 | * they'll probably all be fine on performance. You may want to decide on the special features of a generator, indicated 29 | * by implementing {@link StatefulRandomness} if their state can be read and written to, and/or 30 | * {@link SkippingRandomness} if sections in the generator's sequence can be skipped in long forward or backward leaps. 31 | *
32 | * Original version here. 33 | *
34 | * Written in 2016 by David Blackman and Sebastiano Vigna (vigna@acm.org) 35 | * 36 | * @author Sebastiano Vigna 37 | * @author David Blackman 38 | * @author Tommy Ettinger (if there's a flaw, use SquidLib's issues and don't bother Vigna or Blackman, it's probably a mistake in SquidLib's implementation) 39 | * @author Matthias Nickles (cosmetic changes) 40 | */ 41 | public /*final*/ class XoRoRNGd extends java.util.Random { 42 | 43 | private static final long DOUBLE_MASK = (1L << 53) - 1; 44 | private static final double NORM_53 = 1. / (1L << 53); 45 | private static final long FLOAT_MASK = (1L << 24) - 1; 46 | private static final double NORM_24 = 1. / (1L << 24); 47 | 48 | private static final long serialVersionUID = 1018744536171610262L; 49 | 50 | private long state0, state1; 51 | 52 | /** 53 | * Creates a new generator seeded using four calls to Math.random(). 54 | */ 55 | public XoRoRNGd() { 56 | this((long) ((Math.random() - 0.5) * 0x10000000000000L) 57 | ^ (long) (((Math.random() - 0.5) * 2.0) * 0x8000000000000000L), 58 | (long) ((Math.random() - 0.5) * 0x10000000000000L) 59 | ^ (long) (((Math.random() - 0.5) * 2.0) * 0x8000000000000000L)); 60 | } 61 | /** 62 | * Constructs this XoRoRNG by dispersing the bits of seed using {@link #setSeed(long)} across the two parts of state 63 | * this has. 64 | * @param seed a long that won't be used exactly, but will affect both components of state 65 | */ 66 | public XoRoRNGd(final long seed) { 67 | setSeed(seed); 68 | } 69 | /** 70 | * Constructs this XoRoRNG by calling {@link #setSeed(long, long)} on the arguments as given; see that method for 71 | * the specific details (stateA and stateB are kept as-is unless they are both 0). 72 | * @param stateA the number to use as the first part of the state; this will be 1 instead if both seeds are 0 73 | * @param stateB the number to use as the second part of the state 74 | */ 75 | public XoRoRNGd(final long stateA, final long stateB) { 76 | setSeed(stateA, stateB); 77 | } 78 | 79 | public final int next(int bits) { 80 | final long s0 = state0; 81 | long s1 = state1; 82 | final int result = (int)(s0 + s1) >>> (32 - bits); 83 | s1 ^= s0; 84 | state0 = (s0 << 55 | s0 >>> 9) ^ s1 ^ (s1 << 14); // a, b 85 | state1 = (s1 << 36 | s1 >>> 28); // c 86 | return result; 87 | } 88 | 89 | @Override 90 | public final long nextLong() { 91 | final long s0 = state0; 92 | long s1 = state1; 93 | final long result = s0 + s1; 94 | 95 | s1 ^= s0; 96 | state0 = (s0 << 55 | s0 >>> 9) ^ s1 ^ (s1 << 14); // a, b 97 | state1 = (s1 << 36 | s1 >>> 28); // c 98 | /* 99 | state0 = Long.rotateLeft(s0, 55) ^ s1 ^ (s1 << 14); // a, b 100 | state1 = Long.rotateLeft(s1, 36); // c 101 | */ 102 | return result; 103 | } 104 | 105 | /** 106 | * Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the 107 | * copy, both will generate the same sequence of random numbers from the point copy() was called. This just needs to 108 | * copy the state so it isn't shared, usually, and produce a new value with the same exact state. 109 | * 110 | * @return a copy of this RandomnessSource 111 | */ 112 | 113 | public XoRoRNGd copy() { 114 | XoRoRNGd next = new XoRoRNGd(state0); 115 | next.state0 = state0; 116 | next.state1 = state1; 117 | return next; 118 | } 119 | 120 | 121 | /** 122 | * Can return any int, positive or negative, of any size permissible in a 32-bit signed integer. 123 | * @return any int, all 32 bits are random 124 | */ 125 | @Override 126 | public int nextInt() { 127 | return (int)nextLong(); 128 | } 129 | 130 | /** 131 | * Exclusive on the outer bound; the inner bound is 0. The bound may be negative, which will produce a non-positive 132 | * result. 133 | * @param bound the outer exclusive bound; may be positive or negative 134 | * @return a random int between 0 (inclusive) and bound (exclusive) 135 | */ 136 | @Override 137 | public int nextInt(final int bound) { 138 | return (int) ((bound * (nextLong() >>> 33)) >> 31); 139 | } 140 | /** 141 | * Inclusive lower, exclusive upper. 142 | * @param inner the inner bound, inclusive, can be positive or negative 143 | * @param outer the outer bound, exclusive, should be positive, should usually be greater than inner 144 | * @return a random int that may be equal to inner and will otherwise be between inner and outer 145 | */ 146 | public int nextInt(final int inner, final int outer) { 147 | return inner + nextInt(outer - inner); 148 | } 149 | 150 | /** 151 | * Exclusive on the outer bound; the inner bound is 0. The bound may be negative, which will produce a non-positive 152 | * result. 153 | * @param bound the outer exclusive bound; may be positive or negative 154 | * @return a random long between 0 (inclusive) and bound (exclusive) 155 | */ 156 | public long nextLong(long bound) { 157 | long rand = nextLong(); 158 | final long randLow = rand & 0xFFFFFFFFL; 159 | final long boundLow = bound & 0xFFFFFFFFL; 160 | rand >>>= 32; 161 | bound >>= 32; 162 | final long z = (randLow * boundLow >> 32); 163 | long t = rand * boundLow + z; 164 | final long tLow = t & 0xFFFFFFFFL; 165 | t >>>= 32; 166 | return rand * bound + t + (tLow + randLow * bound >> 32) - (z >> 63) - (bound >> 63); 167 | } 168 | /** 169 | * Inclusive inner, exclusive outer; both inner and outer can be positive or negative. 170 | * @param inner the inner bound, inclusive, can be positive or negative 171 | * @param outer the outer bound, exclusive, can be positive or negative and may be greater than or less than inner 172 | * @return a random long that may be equal to inner and will otherwise be between inner and outer 173 | */ 174 | public long nextLong(final long inner, final long outer) { 175 | return inner + nextLong(outer - inner); 176 | } 177 | 178 | @Override 179 | public double nextDouble() { 180 | return (nextLong() & DOUBLE_MASK) * NORM_53; 181 | } 182 | 183 | @Override 184 | public float nextFloat() { 185 | return (float) ((nextLong() & FLOAT_MASK) * NORM_24); 186 | } 187 | 188 | @Override 189 | public boolean nextBoolean() { 190 | return nextLong() < 0L; 191 | } 192 | 193 | @Override 194 | public void nextBytes(final byte[] bytes) { 195 | int i = bytes.length, n = 0; 196 | while (i != 0) { 197 | n = Math.min(i, 8); 198 | for (long bits = nextLong(); n-- != 0; bits >>>= 8) { 199 | bytes[--i] = (byte) bits; 200 | } 201 | } 202 | } 203 | 204 | 205 | /** 206 | * Sets the seed of this generator using one long, running that through LightRNG's algorithm twice to get the state. 207 | * @param seed the number to use as the seed 208 | */ 209 | @Override 210 | public void setSeed(final long seed) { 211 | 212 | long state = seed + 0x9E3779B97F4A7C15L, 213 | z = state; 214 | z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L; 215 | z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL; 216 | state0 = z ^ (z >>> 31); 217 | state += 0x9E3779B97F4A7C15L; 218 | z = state; 219 | z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L; 220 | z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL; 221 | state1 = z ^ (z >>> 31); 222 | } 223 | 224 | /** 225 | * Sets the seed of this generator using two longs, using them without changes unless both are 0 (then it makes the 226 | * state variable corresponding to stateA 1 instead). 227 | * @param stateA the number to use as the first part of the state; this will be 1 instead if both seeds are 0 228 | * @param stateB the number to use as the second part of the state 229 | */ 230 | public void setSeed(final long stateA, final long stateB) { 231 | 232 | state0 = stateA; 233 | state1 = stateB; 234 | if((stateA | stateB) == 0L) 235 | state0 = 1L; 236 | } 237 | 238 | /** 239 | * Gets the first component of this generator's two-part state, as a long. This can be 0 on its own, but will never 240 | * be 0 at the same time as the other component of state, {@link #getStateB()}. You can set the state with two exact 241 | * values using {@link #setSeed(long, long)}, but the alternative overload {@link #setSeed(long)} won't use the 242 | * state without changing it (it needs to cover 128 bits with a 64-bit value). 243 | * @return the first component of this generator's state 244 | */ 245 | public long getStateA() 246 | { 247 | return state0; 248 | } 249 | /** 250 | * Gets the second component of this generator's two-part state, as a long. This can be 0 on its own, but will never 251 | * be 0 at the same time as the other component of state, {@link #getStateA()}. You can set the state with two exact 252 | * values using {@link #setSeed(long, long)}, but the alternative overload {@link #setSeed(long)} won't use the 253 | * state without changing it (it needs to cover 128 bits with a 64-bit value). 254 | * @return the second component of this generator's state 255 | */ 256 | public long getStateB() 257 | { 258 | return state1; 259 | } 260 | 261 | @Override 262 | public String toString() { 263 | return "XoRoRNGd"; 264 | } 265 | 266 | @Override 267 | public boolean equals(Object o) { 268 | if (this == o) return true; 269 | if (o == null || getClass() != o.getClass()) return false; 270 | 271 | XoRoRNGd xoRoRNG = (XoRoRNGd) o; 272 | 273 | if (state0 != xoRoRNG.state0) return false; 274 | return state1 == xoRoRNG.state1; 275 | } 276 | 277 | @Override 278 | public int hashCode() { 279 | return (int) (31L * (state0 ^ (state0 >>> 32)) + (state1 ^ (state1 >>> 32))); 280 | } 281 | } 282 | --------------------------------------------------------------------------------