Skip to content

Conversation

elteammate
Copy link
Collaborator

Depends on #62. Implements a public wrapper for solver in Kosat class.

I have chosen to sacrifice a little bit of performance with Iterable<Int> instead of List<Int> and a little bit of code duplication. I don't think performance should be a concern at this point, as memory is reallocated into arrays anyway, at least twice in the solver itself.

Furthermore, I have also decided to include rigorous checks into methods (like enforcing allocating variables before adding clauses) because it will be easier to undo this decision in the future (as opposed to adding the checks later and introducing a breaking change~ well, not really, it's not like it's specified anywhere, might be ok to bump patch).

Unfortunately, I still was not able to include Java tests in the same project, so I have only tested the interface from java world manually. It looks ok, but if writing tests in Java is necessary, I will figure something out.

@elteammate elteammate requested a review from Lipen September 6, 2023 20:36
sourceSets {
commonMain {
dependencies {
implementation(project(":core"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The module naming (and splitting, in the first place) is weird. API should NOT depend on the implementation, but the "implementation" should implement the public interface contract. Note that the "implementation" has a separate choice of making API dependency truly api (in Gradle terms, api vs implementation configuration), however it is not "mandatory": the implementation might choose to comply with the interface without actually letting us know about it.
One example of a "good" api/impl split is SLF4J and its implementations (e.g. slf4j-simple). The same goes for log4j, if you prefer it.

That way, ideally, if the user only wants the interface, it declares the dependency on api module and receives only that - bare interface. However, when the user wants some kinda implementation, it declares the dependency on "core"/"impl" module, and receives the working classes. If he wants to interact with this implementation via some fancy interface, it should either additionally declare a dependency on API-providing module, or force "core"/"impl" to provide it via api Gradle-configuration.

Currently, your "kosat-api" module provides a mere "fancy wrapper" for CDCL, not an actual API.
See https://github.com/Lipen/sat-nexus/blob/master/lib/wrappers/src/wrap_minisat.rs


All in all, I would suggest NOT splitting impl into "core" and "wrapper", since it requires the duplicate multiplatform setup, and does not provide any value. Simply provide both "internal solver" (CDCL) and "external solver" (KoSAT) in the same module (and even in the same package org.kosat.*).

* indexing). All variables must be defined with [newVariable] before adding
* clauses.
*/
fun newClause(vararg literals: Int) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the users that already have an IntArray of literals? Are we going to force them to use *-spread operator solver.newClause(*literals) ?

* clauses.
*/
fun newClause(vararg literals: Int) {
newClause(literals.asIterable())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably don't want to allow users override this method.


/**
* Solve the SAT problem with the given assumptions, if any. Assumptions are
* literals in DIMACS format.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only "vararg" version of solve has the comment about DIMACS literals. Instead, it should be noted in each method, or simply in the class docs.

}

/**
* Solve the SAT problem with the given assumptions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document the return type.
true = SAT (ok to query the model)
false = UNSAT or UNKNOWN (model querying is prohibited)

/**
* Solve the SAT problem with the given assumptions
*/
fun solve(assumptions: Iterable<Int>): Boolean {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about UNKNOWN results? Budget exhaustion and async interrupts might lead to the solver state with is neither SAT (has a satisfying model) nor UNSAT (truly proved unsatisfiability).

*/
fun value(literal: Int): Boolean {
if (literal == 0) {
throw IllegalArgumentException("Literal must not be 0")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be written via require (it throws IllegalArgumentException, contrary to check which throws IllegalStateException).

fun tieShirtVarargs() {
val solver = Kosat()

assertFailsWith(IllegalArgumentException::class) { solver.newClause(1, 2) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about assertFailsWith<IllegalStateException> { ... } ? No need for ::class when we have reified :)

assertTrue(solver.solve(setOf(-1)))
assertFalse(solver.solve(listOf(1)))
assertTrue(solver.solve(listOf(-1, 2)))
assertFalse(solver.solve(setOf(-1, -2)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about testing the vararg (and IntArray, if you happen to choose to split them as such) solve method?

myInclude("core")
myInclude("cli")
myInclude("web")
myInclude("api")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"wrapper", see the first comment in the review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants