Skip to documentation
SLOP

tiny.smt.dimacs

Reference tiny.smt dimacs

Defined in tiny.smt.

Reads and writes Boolean formulas in conjunctive normal form as plain text.

API (3)

Actions

Public operations.

Types and contracts

Public types and contracts.

No direct callersNo direct callstiny.smtdimacs
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsNo direct callstest sourcelib.smt.src.dimacstest: DIMACS parser builds satisfiabl...test sourcelib.smt.src.dimacstest: DIMACS writer preserves the emp...dimacsparse
Static calls · unresolved targets: 1 · external targets: 9.
Called byCallsNo direct callstest sourcelib.smt.src.dimacstest: DIMACS writer preserves the emp...test sourcelib.smt.src.dimacstest: DIMACS writer roundtrips clausesdimacswrite
Static calls · unresolved targets: 1 · external targets: 4.

Source: lib/smt/src/dimacs.zig

zig
//! Reads and writes Boolean formulas in conjunctive normal form as plain text. The format is DIMACS//! CNF: a `p cnf V C` header gives the variable and clause counts, `c` lines are comments, and each//! clause lists its literals as signed variable numbers, 1-based, ending with 0.//!//! Other SAT tools and benchmark collections exchange formulas as text, so a caller needs to load//! such a file into a solver and to print a solver's clauses for another tool. A text written//! elsewhere may be malformed, or may disagree with its own header about how many clauses it holds.//!//! Variable 1 of the text is the solver's variable 0, and a negative number is the negated literal.//! The reader treats the header's clause count as a check and its variable count as a floor: a//! clause may name a variable past the count, and the solver grows to hold it. The writer prints//! every clause the solver holds, learned clauses the solver kept included, so a text written after//! a solve can hold more clauses than were added.const std = @import("std");const sat = @import("sat/root.zig");/// The errors `parse` returns for a malformed text, besides the allocator's and the solver's, so/// code that reports why a DIMACS text was refused can switch on them. A header count that fails to/// parse as a number returns the error of `std.fmt.parseInt`, which this set leaves out. `parse`/// returns an inferred error set.pub const ParseError = error{    /// A `p` line whose kind differs from `cnf` or that lacks a count.    InvalidHeader,    /// A clause token other than a 32-bit signed integer, or a last clause missing a closing 0.    InvalidLiteral,    /// The number of clauses differs from the header's count.    ClauseCountMismatch,};/// Returns a new solver that allocates with `allocator` and holds the variables and clauses of the/// DIMACS text `source`, so code can load a formula written by another tool, then solve the/// returned solver. The caller owns the solver and frees it with `deinit`. Lines are trimmed of/// spaces, tabs and carriage returns, and blank lines and lines starting with `c` are skipped. A/// `p cnf V C` line adds V variables and sets C as the expected clause count. The parser accepts a/// text without a header, and a second header adds its variables and replaces the expected count. A/// clause may span lines, and one line may hold more than one clause: each 0 ends one. A lone 0 is/// the empty clause. The call returns `InvalidHeader`, `InvalidLiteral` and `ClauseCountMismatch`/// as their tags describe, and the errors of `std.fmt.parseInt` for a header count that fails to/// parse as a number. The literal -2147483648 makes the call panic.pub fn parse(allocator: std.mem.Allocator, source: []const u8) !sat.Solver {    var solver = sat.Solver.init(allocator);    errdefer solver.deinit();    var expected_clauses: ?usize = null;    var clauses_seen: usize = 0;    var clause: std.ArrayList(sat.Literal) = .empty;    defer clause.deinit(allocator);    var lines = std.mem.splitScalar(u8, source, '\n');    while (lines.next()) |raw_line| {        const line = std.mem.trim(u8, raw_line, " \t\r");        if (line.len == 0 or line[0] == 'c') continue;        var tokens = std.mem.tokenizeAny(u8, line, " \t\r");        const first = tokens.next() orelse continue;        if (std.mem.eql(u8, first, "p")) {            const kind = tokens.next() orelse return ParseError.InvalidHeader;            if (!std.mem.eql(u8, kind, "cnf")) return ParseError.InvalidHeader;            const variable_count = try std.fmt.parseInt(usize, tokens.next() orelse return ParseError.InvalidHeader, 10);            expected_clauses = try std.fmt.parseInt(usize, tokens.next() orelse return ParseError.InvalidHeader, 10);            var index: usize = 0;            while (index < variable_count) : (index += 1) _ = try solver.addVariable();            continue;        }        var current: ?[]const u8 = first;        while (current) |token| {            const value = std.fmt.parseInt(i32, token, 10) catch return ParseError.InvalidLiteral;            if (value == 0) {                try solver.addClause(clause.items);                clause.clearRetainingCapacity();                clauses_seen += 1;            } else {                try clause.append(allocator, try sat.Literal.fromDimacs(value));            }            current = tokens.next();        }    }    if (clause.items.len > 0) return ParseError.InvalidLiteral;    if (expected_clauses) |expected| {        if (expected != clauses_seen) return ParseError.ClauseCountMismatch;    }    return solver;}/// Writes `solver` as DIMACS CNF: a `p cnf V C` header, then one line per clause of its literals/// and a closing 0, so code can hand a solver's formula to another tool. An empty clause added to/// the solver appears once, as a line holding only 0, and the header counts it. The function writes/// every clause the solver holds, learned clauses the solver kept included. The call returns only/// the writer's errors.pub fn write(writer: *std.Io.Writer, solver: *const sat.Solver) std.Io.Writer.Error!void {    const empty_clauses: usize = @intFromBool(solver.has_empty_clause);    try writer.print("p cnf {d} {d}\n", .{        solver.variableCount(),        solver.clauseCount() + empty_clauses,    });    if (solver.has_empty_clause) {        try writer.writeAll("0\n");    }    for (solver.clauses.items) |clause| {        for (clause.literals) |literal| {            try writer.print("{d} ", .{literal.toDimacs()});        }        try writer.writeAll("0\n");    }}test "DIMACS writer preserves the empty clause" {    var solver = sat.Solver.init(std.testing.allocator);    defer solver.deinit();    _ = try solver.addVariable();    try solver.addClause(&.{sat.Literal.positive(0)});    try solver.addClause(&.{});    var buffer: [64]u8 = undefined;    var stream = std.Io.Writer.fixed(&buffer);    try write(&stream, &solver);    const text = stream.buffered();    try std.testing.expect(std.mem.indexOf(u8, text, "p cnf 1 2\n") != null);    try std.testing.expect(std.mem.indexOf(u8, text, "\n0\n") != null);    var parsed = try parse(std.testing.allocator, text);    defer parsed.deinit();    try std.testing.expectEqual(sat.Status.unsat, try parsed.solve());}test "DIMACS parser builds satisfiable formula" {    var solver = try parse(std.testing.allocator,        \\c example        \\p cnf 2 2        \\1 2 0        \\-1 0        \\    );    defer solver.deinit();    try std.testing.expectEqual(sat.Status.sat, try solver.solve());}test "DIMACS writer roundtrips clauses" {    var solver = sat.Solver.init(std.testing.allocator);    defer solver.deinit();    const a = try solver.addVariable();    const b = try solver.addVariable();    try solver.addClause(&.{ sat.Literal.positive(a), sat.Literal.negative(b) });    var buffer: [256]u8 = undefined;    var stream = std.Io.Writer.fixed(&buffer);    try write(&stream, &solver);    const text = stream.buffered();    try std.testing.expect(std.mem.indexOf(u8, text, "p cnf 2 1\n") != null);    try std.testing.expect(std.mem.indexOf(u8, text, "1 -2 0\n") != null);}

Source: lib/smt/src/root.zig:97

zig
pub const dimacs = @import("dimacs.zig");

Audit

Definitions4
Public names4
Members3
Version26.7.0
Revisiondaab053ee433