We are trying to use z3 to generate proofs, through the C API (through ScalaZ3).
we thus enable the proof generation by setting PROOF=true. This however affects the result of other checks. Note that we do not use tactics/goals, we simply create a new solver and assert constraints and check.
For instance, on the following constraint (where x is of sort Integer):
(x + x) != (2 * x)
check() returns UNSAT with PROOF=false as expected. With PROOF=true however, it returns SAT with model (x -> 1).
We do not specify any logic in particular. We tried explicitly requesting a solver over QF_AUFLIA, but it's unclear whether the logic is silently ignored/unknown, or if it has no effect on this issue. It looks as if + becomes uninterpreted when proofs-production is requested.
Are we missing something obvious with this proof_generation mode?
EDIT 2:
We are using latest unstable from git (5b5a474b5443) in debug mode.
Here is the trace we get with PROOF=true:
R
C 3
= 0x7f04a0091a08
R
P 0x7f04a0091a08
S "MODEL"
S "true"
C 5
R
P 0x7f04a0091a08
S "TYPE_CHECK"
S "true"
C 5
R
P 0x7f04a0091a08
S "PROOF"
S "true"
C 5
R
P 0x7f04a0091a08
S "WELL_SORTED_CHECK"
S "true"
C 5
R
I 1
C 273
R
P 0x7f04a0091a08
C 7
= 0x7f04a00abda8
R
P 0x7f04a00abda8
C 33
= 0x7f04a00915e8
R
P 0x7f04a00abda8
P 0x7f04a00915e8
C 9
R
P 0x7f04a00abda8
C 32
= 0x7f04a007a2c8
R
P 0x7f04a00abda8
P 0x7f04a007a2c8
C 9
R
P 0x7f04a00abda8
S "Unit"
C 30
R
P 0x7f04a00abda8
S "Unit"
C 30
R
P 0x7f04a00abda8
S "isUnit"
C 30
R
P 0x7f04a00abda8
$ |Unit|
$ |isUnit|
U 0
s 0
p 0
u 0
C 41
= 0x7f04a00ab4c8
R
P 0x7f04a00abda8
U 1
P 0x7f04a00ab4c8
p 1
C 44
= 0x7f04a006c218
R
P 0x7f04a00abda8
U 1
$ |Unit|
s 1
P 0
p 1
P 0x7f04a006c218
p 1
C 46
# 0x7f04a00ab1c8 3 0
# 0x7f04a006c218 4 0
R
P 0x7f04a00abda8
P 0x7f04a006c218
C 45
R
P 0x7f04a00abda8
P 0x7f04a00ab4c8
U 0
P 0
P 0
p 0
C 47
* 0x7f04a00ab2f8 3
* 0x7f04a00ab0f8 4
R
P 0x7f04a00abda8
P 0x7f04a00ab2f8
C 9
R
P 0x7f04a00abda8
P 0x7f04a00ab0f8
C 9
R
P 0x7f04a00abda8
P 0x7f04a00ab1c8
C 9
R
P 0x7f04a00abda8
P 0x7f04a00ab2f8
U 0
p 0
C 49
= 0x7f04a006ab98
R
P 0x7f04a00abda8
P 0x7f04a006ab98
C 9
R
P 0x7f04a00abda8
C 32
= 0x7f04a007a2c8
R
P 0x7f04a00abda8
P 0x7f04a00915e8
P 0x7f04a007a2c8
C 37
= 0x7f04a00ab538
R
P 0x7f04a00abda8
P 0x7f04a00ab538
C 9
R
P 0x7f04a00abda8
S "card"
U 1
P 0x7f04a00ab538
p 1
P 0x7f04a00915e8
C 51
= 0x7f04a007a588
R
P 0x7f04a00abda8
P 0x7f04a007a588
C 9
R
P 0x7f04a00abda8
S "setMin"
U 1
P 0x7f04a00ab538
p 1
P 0x7f04a00915e8
C 51
= 0x7f04a00ab3e8
R
P 0x7f04a00abda8
P 0x7f04a00ab3e8
C 9
R
P 0x7f04a00abda8
S "setMax"
U 1
P 0x7f04a00ab538
p 1
P 0x7f04a00915e8
C 51
= 0x7f04a00ab068
R
P 0x7f04a00abda8
P 0x7f04a00ab068
C 9
R
P 0x7f04a00abda8
U 0
s 0
p 0
p 0
C 46
R
P 0x7f04a00abda8
S "QF_AUFLIA"
C 30
R
P 0x7f04a00abda8
$ |QF_AUFLIA|
C 412
= 0x7f04a00bfb38
R
P 0x7f04a00abda8
P 0x7f04a00bfb38
C 417
R
P 0x7f04a00abda8
S "start0"
P 0x7f04a007a2c8
C 52
= 0x7f04a00bfc38
R
P 0x7f04a00abda8
P 0x7f04a00bfc38
C 9
R
P 0x7f04a00abda8
S "x0"
P 0x7f04a00915e8
C 52
= 0x7f04a00a55a8
R
P 0x7f04a00abda8
P 0x7f04a00a55a8
C 9
R
P 0x7f04a00abda8
U 2
P 0x7f04a00a55a8
P 0x7f04a00a55a8
p 2
C 64
= 0x7f04a00a51a8
R
P 0x7f04a00abda8
P 0x7f04a00a51a8
C 9
R
P 0x7f04a00abda8
I 2
P 0x7f04a00915e8
C 145
= 0x7f04a00a5528
R
P 0x7f04a00abda8
P 0x7f04a00a5528
C 9
R
P 0x7f04a00abda8
U 2
P 0x7f04a00a5528
P 0x7f04a00a55a8
p 2
C 65
= 0x7f04a00bf478
R
P 0x7f04a00abda8
P 0x7f04a00bf478
C 9
R
P 0x7f04a00abda8
U 2
P 0x7f04a00a51a8
P 0x7f04a00bf478
p 2
C 56
= 0x7f04a00bfd48
R
P 0x7f04a00abda8
P 0x7f04a00bfd48
C 9
R
P 0x7f04a00abda8
P 0x7f04a00bfc38
P 0x7f04a00bfd48
C 60
= 0x7f04a00bfda8
R
P 0x7f04a00abda8
P 0x7f04a00bfda8
C 9
R
P 0x7f04a00abda8
S "x0"
P 0x7f04a00915e8
C 52
= 0x7f04a00a50d8
R
P 0x7f04a00abda8
P 0x7f04a00a50d8
C 9
R
P 0x7f04a00abda8
P 0x7f04a00bfda8
U 2
P 0x7f04a00a55a8
P 0x7f04a00bfc38
p 2
P 0x7f04a00a50d8
P 0x7f04a00bfc38
p 2
C 245
= 0x7f04a00018d8
R
P 0x7f04a00abda8
P 0x7f04a00018d8
C 9
R
P 0x7f04a00abda8
P 0x7f04a00bfb38
P 0x7f04a00bfc38
C 423
R
P 0x7f04a00abda8
P 0x7f04a00bfb38
P 0x7f04a00018d8
C 423
R
P 0x7f04a00abda8
P 0x7f04a00bfb38
C 419
R
P 0x7f04a00abda8
P 0x7f04a00bfb38
U 0
p 0
C 427
R
P 0x7f04a00abda8
P 0x7f04a00bfb38
U 1
C 420
R
P 0x7f04a00abda8
P 0x7f04a00bfb38
C 428
= 0x7f04a011a9e8
R
P 0x7f04a00abda8
P 0x7f04a011a9e8
C 248
R
P 0x7f04a00abda8
P 0x7f04a011a9e8
P 0x7f04a00a50d8
I 0
P 0
C 250
* 0x7f04a00ff678 4
R
P 0x7f04a00abda8
P 0x7f04a00ff678
C 9
R
P 0x7f04a00abda8
P 0x7f04a00ff678
I 0
C 219
Here is the trace we get with PROOF=false:
R
C 3
= 0x7f489c03aef8
R
P 0x7f489c03aef8
S "MODEL"
S "true"
C 5
R
P 0x7f489c03aef8
S "TYPE_CHECK"
S "true"
C 5
R
P 0x7f489c03aef8
S "PROOF"
S "false"
C 5
R
P 0x7f489c03aef8
S "WELL_SORTED_CHECK"
S "true"
C 5
R
I 1
C 273
R
P 0x7f489c03aef8
C 7
= 0x7f489c06fe48
R
P 0x7f489c06fe48
C 33
= 0x7f489c0acba8
R
P 0x7f489c06fe48
P 0x7f489c0acba8
C 9
R
P 0x7f489c06fe48
C 32
= 0x7f489c0112a8
R
P 0x7f489c06fe48
P 0x7f489c0112a8
C 9
R
P 0x7f489c06fe48
S "Unit"
C 30
R
P 0x7f489c06fe48
S "Unit"
C 30
R
P 0x7f489c06fe48
S "isUnit"
C 30
R
P 0x7f489c06fe48
$ |Unit|
$ |isUnit|
U 0
s 0
p 0
u 0
C 41
= 0x7f489c0ccff8
R
P 0x7f489c06fe48
U 1
P 0x7f489c0ccff8
p 1
C 44
= 0x7f489c010c38
R
P 0x7f489c06fe48
U 1
$ |Unit|
s 1
P 0
p 1
P 0x7f489c010c38
p 1
C 46
# 0x7f489c0ae2f8 3 0
# 0x7f489c010c38 4 0
R
P 0x7f489c06fe48
P 0x7f489c010c38
C 45
R
P 0x7f489c06fe48
P 0x7f489c0ccff8
U 0
P 0
P 0
p 0
C 47
* 0x7f489c0c3ae8 3
* 0x7f489c0736e8 4
R
P 0x7f489c06fe48
P 0x7f489c0c3ae8
C 9
R
P 0x7f489c06fe48
P 0x7f489c0736e8
C 9
R
P 0x7f489c06fe48
P 0x7f489c0ae2f8
C 9
R
P 0x7f489c06fe48
P 0x7f489c0c3ae8
U 0
p 0
C 49
= 0x7f489c0be318
R
P 0x7f489c06fe48
P 0x7f489c0be318
C 9
R
P 0x7f489c06fe48
C 32
= 0x7f489c0112a8
R
P 0x7f489c06fe48
P 0x7f489c0acba8
P 0x7f489c0112a8
C 37
= 0x7f489c0b5c18
R
P 0x7f489c06fe48
P 0x7f489c0b5c18
C 9
R
P 0x7f489c06fe48
S "card"
U 1
P 0x7f489c0b5c18
p 1
P 0x7f489c0acba8
C 51
= 0x7f489c0739f8
R
P 0x7f489c06fe48
P 0x7f489c0739f8
C 9
R
P 0x7f489c06fe48
S "setMin"
U 1
P 0x7f489c0b5c18
p 1
P 0x7f489c0acba8
C 51
= 0x7f489c0cc1a8
R
P 0x7f489c06fe48
P 0x7f489c0cc1a8
C 9
R
P 0x7f489c06fe48
S "setMax"
U 1
P 0x7f489c0b5c18
p 1
P 0x7f489c0acba8
C 51
= 0x7f489c073768
R
P 0x7f489c06fe48
P 0x7f489c073768
C 9
R
P 0x7f489c06fe48
U 0
s 0
p 0
p 0
C 46
R
P 0x7f489c06fe48
S "QF_AUFLIA"
C 30
R
P 0x7f489c06fe48
$ |QF_AUFLIA|
C 412
= 0x7f489c0714f8
R
P 0x7f489c06fe48
P 0x7f489c0714f8
C 417
R
P 0x7f489c06fe48
S "start0"
P 0x7f489c0112a8
C 52
= 0x7f489c0d5488
R
P 0x7f489c06fe48
P 0x7f489c0d5488
C 9
R
P 0x7f489c06fe48
S "x0"
P 0x7f489c0acba8
C 52
= 0x7f489c0c37d8
R
P 0x7f489c06fe48
P 0x7f489c0c37d8
C 9
R
P 0x7f489c06fe48
U 2
P 0x7f489c0c37d8
P 0x7f489c0c37d8
p 2
C 64
= 0x7f489c0d07b8
R
P 0x7f489c06fe48
P 0x7f489c0d07b8
C 9
R
P 0x7f489c06fe48
I 2
P 0x7f489c0acba8
C 145
= 0x7f489c0cca98
R
P 0x7f489c06fe48
P 0x7f489c0cca98
C 9
R
P 0x7f489c06fe48
U 2
P 0x7f489c0cca98
P 0x7f489c0c37d8
p 2
C 65
= 0x7f489c0b0c08
R
P 0x7f489c06fe48
P 0x7f489c0b0c08
C 9
R
P 0x7f489c06fe48
U 2
P 0x7f489c0d07b8
P 0x7f489c0b0c08
p 2
C 56
= 0x7f489c0cbb78
R
P 0x7f489c06fe48
P 0x7f489c0cbb78
C 9
R
P 0x7f489c06fe48
P 0x7f489c0d5488
P 0x7f489c0cbb78
C 60
= 0x7f489c0cbbd8
R
P 0x7f489c06fe48
P 0x7f489c0cbbd8
C 9
R
P 0x7f489c06fe48
S "x0"
P 0x7f489c0acba8
C 52
= 0x7f489c181a78
R
P 0x7f489c06fe48
P 0x7f489c181a78
C 9
R
P 0x7f489c06fe48
P 0x7f489c0cbbd8
U 2
P 0x7f489c0c37d8
P 0x7f489c0d5488
p 2
P 0x7f489c181a78
P 0x7f489c0d5488
p 2
C 245
= 0x7f489c181a28
R
P 0x7f489c06fe48
P 0x7f489c181a28
C 9
R
P 0x7f489c06fe48
P 0x7f489c0714f8
P 0x7f489c0d5488
C 423
R
P 0x7f489c06fe48
P 0x7f489c0714f8
P 0x7f489c181a28
C 423
R
P 0x7f489c06fe48
P 0x7f489c0714f8
C 419
R
P 0x7f489c06fe48
P 0x7f489c0714f8
U 0
p 0
C 427
R
P 0x7f489c06fe48
P 0x7f489c0714f8
U 1
C 420
R
P 0x7f489c06fe48
P 0x7f489c0714f8
C 430
= 0x7f489c0cc9f8
R
P 0x7f489c06fe48
P 0x7f489c0cc9f8
C 330
R
P 0x7f489c06fe48
P 0x7f489c0cc9f8
C 332
EDIT 3:
I created a small C program that reproduces the issue:
#include <stdlib.h>
#include <stdio.h>
#include <z3.h>
int main(int argc, char **args) {
Z3_open_log("z3.log");
int proofMode = (argc > 1);
Z3_config conf = Z3_mk_config();
Z3_set_param_value(conf, "MODEL", "true");
Z3_set_param_value(conf, "TYPE_CHECK", "true");
Z3_set_param_value(conf, "WELL_SORTED_CHECK", "true");
if (proofMode) {
Z3_set_param_value(conf, "PROOF", "true");
} else {
Z3_set_param_value(conf, "PROOF", "false");
}
Z3_context ctx = Z3_mk_context_rc(conf);
Z3_solver solver = Z3_mk_solver(ctx);
Z3_sort boolSort = Z3_mk_bool_sort(ctx);
Z3_sort intSort = Z3_mk_int_sort(ctx);
Z3_ast x = Z3_mk_fresh_const(ctx, "x", intSort);
Z3_inc_ref(ctx, x);
Z3_ast newX = Z3_mk_fresh_const(ctx, "x", intSort);
Z3_inc_ref(ctx, newX);
Z3_ast c2 = Z3_mk_int(ctx, 2, intSort);
Z3_inc_ref(ctx, c2);
Z3_ast start = Z3_mk_fresh_const(ctx, "start", boolSort);
Z3_inc_ref(ctx, start);
Z3_ast xpx_args[2] = {x, x};
Z3_ast xpx = Z3_mk_add(ctx, 2, xpx_args);
Z3_inc_ref(ctx, xpx);
Z3_ast xt2_args[2] = {c2, x};
Z3_ast xt2 = Z3_mk_mul(ctx, 2, xt2_args);
Z3_inc_ref(ctx, xt2);
Z3_ast dis_args[2] = {xpx, xt2};
Z3_ast dis = Z3_mk_distinct(ctx, 2, dis_args);
Z3_inc_ref(ctx, dis);
Z3_ast res = Z3_mk_implies(ctx, start, dis);
Z3_inc_ref(ctx, res);
printf("AST Before: %s\n", Z3_ast_to_string(ctx, res));
// Substituting
Z3_ast substRes = Z3_substitute(ctx, res, 1, &x, &newX);
Z3_inc_ref(ctx, substRes);
printf("AST After: %s\n", Z3_ast_to_string(ctx, substRes));
// Solving
Z3_solver_assert(ctx, solver, start);
Z3_solver_assert(ctx, solver, substRes);
Z3_lbool solverRes = Z3_solver_check(ctx, solver);
printf("Solver (proofMode=%s) returned %s\n", proofMode ? "true" : "false", (solverRes == Z3_L_FALSE ? "UNSAT" : (solverRes == Z3_L_TRUE ? "SAT" : "UNKNOWN")));
return 0;
}
With the following Makefile:
all: compile runproof runnoproof
compile:
gcc -o test -I../z3/src/api/ -L ../z3/build/ -lz3 -g -fPIC -O2 -fopenmp test.c
runproof: compile
LD_LIBRARY_PATH="../z3/build/" ./test proof
runnoproof: compile
LD_LIBRARY_PATH="../z3/build/" ./test
I get the following results against latest z3 unstable:
$ make
gcc -o test -I../z3/src/api/ -L ../z3/build/ -lz3 -g -fPIC -O2 -fopenmp test.c
LD_LIBRARY_PATH="../z3/build/" ./test proof
AST Before: (=> start!2 (distinct (+ x!0 x!0) (* 2 x!0)))
AST After: (or (not start!2) (not (= (* 2 x!1) (* 2 x!0))))
Solver (proofMode=true) returned SAT
LD_LIBRARY_PATH="../z3/build/" ./test
AST Before: (=> start!2 (distinct (+ x!0 x!0) (* 2 x!0)))
AST After: (not start!2)
Solver (proofMode=false) returned UNSAT
The formulas sent to the solver object are different in the two different logs.
In the first log, the following formulas are asserted:
start0!3
(or (not start0!3) (not (= (* 2 x0!5) (* 2 x0!4))))
Z3 correctly reports sat.
In the second log, the following formulas are asserted:
start0!3
(not start0!3)
Could you check if the logs were correctly generated?
You can also trace your own asserts by printing them before each "assert" call.
I also executed the logs using Valgrind, and no memory corruption was detected.
Related
Given N equations in K variables,
can Maxima produce N-J equations in K-J variables?
SOLVE and ELIMINATE seem unable, about to reach for pen and paper.
(%i1) elim ([a = x + y, b = y + z, c = z + x, a = b * c], [a, b, c]);
(%o1) elim([a = y + x, b = z + y, c = z + x, a = b c], [a, b, c])
(%i2) load (to_poly);
(%o2) ~/maxima-5.44.0/share/to_poly_solve/to_poly.lisp
(%i3) elim ([a = x + y, b = y + z, c = z + x, a = b * c], [a, b, c]);
2
(%o3) [[z + (y + x) z + (x - 1) y - x],
[b z - y + b x - x, z + x - c, y + x - a]]
(%i4) solve (first (%o3), x);
2
z + y z - y
(%o4) [x = - ------------]
z + y - 1
I have an matrix in Maxima, let´s say (for simplification of the problem):
A: matrix([2*(a^2+b^2+c^2)])
But I know that:
a^2+b^2+c^2 = 1
How do I simplify that matrix in Maxima in terms of that equation, in order to obtain A = [2]?
I found the solution:
A: matrix([2*(a^2+b^2+c^2)]);
eq: a^2+b^2+c^2 = 1;
scsimp(A, eq);
You can use tellrat.
(%i1) A:matrix([2*(a^2+b^2+c^2)])
(%o1) [ 2 2 2 ]
[ 2 (c + b + a ) ]
(%i2) a^2+b^2+c^2 = 1
2 2 2
(%o2) c + b + a = 1
(%i3) solve(%,a^2)
2 2 2
(%o3) [a = (- c ) - b + 1]
(%i4) tellrat(%[1])
2 2 2
(%o4) [c + b + a - 1]
(%i5) algebraic:true
(%o5) true
(%i6) rat(A)
(%o6)/R/ [ 2 ]
(%i7) untellrat(a)
(%o7) []
I'd like to make a Find-A-Word for the library wall.
The solution (also for the wall) requires a box, enclosing the word.
Horizontal/vertical boxes are no problem. Sloped (slanted?) boxes are what's the problem.\I envisage a command like
\makebox (length, breadth, angle, co-ordinates of left-lower corner)
It may be that this has been done before.
Has anyone any suggestions?
William.
Using tikz and the tikzmarks library:
\documentclass{article}
\usepackage[hmargin=4cm]{geometry}
\usepackage{tikz}
\usetikzlibrary{tikzmark}
\begin{document}
\ttfamily
\noindent
\tikzmark{a:start}w d e w e r n \tikzmark{b:start}b v c w o i q\tikzmark{b:stop} v o i q t u h n r g j q v r o e q i o n j v k w
o q p i n t o j v k m o q e i n g k m f o r q e p i n k f m p i g n o j k m
f \tikzmark{c:start}v e p o q i o n j r g k m l e v q n b g j k v m e q n b o j g k v l m e q
n b j g f k l e m v q n j g k e m l v q n g j r f e l v q n j g f l k q g j
n\tikzmark{a:stop} v q e\tikzmark{c:stop} n p m k w g j k n e k e m l v q n g j r f e l v q n j g f l k q g j
\begin{tikzpicture}[remember picture, overlay]
\draw[red] ([shift={(0,1.5ex)}]pic cs:a:start) rectangle (pic cs:a:stop);
\draw[red] ([shift={(0,1.5ex)}]pic cs:b:start) rectangle (pic cs:b:stop);
\draw[red,rotate=45] ([shift={(1.5ex,1.5ex)}]pic cs:c:start) rectangle ([shift={(-0.5ex,0ex)}]pic cs:c:stop);
\end{tikzpicture}
\end{document}
I am trying to simplify a differential equation via substitution in maxima. However, the substitution does not seem to be working.
Here's my code:
depends (\rho,[t, r, \theta, z]); depends (V, [t, r, \theta, z]);
f_contin : diff (\rho, t) + diff (\rho*r*V[r], r)*(1/r) = 0;
base : diff (V[b]*r*\rho, r) = 0;
V_sub : V[r] = V[b] + \epsilon*V[r];
subst (V_sub, f_contin);
subst (base, %o6);
The last substitution did not work. What am I doing wrong here?
For clarity I add a Screenshot here:
The problem is that subst(a=b, c) (or equivalently subst(b, a, c)) can only make substitutions when a is an exact subexpression of c.
ratsubst (which see) can handle some cases when a is not an exact subexepression but in this case it doesn't seem to work.
But I think you can get the result you want by just subtracting the one equation from the other. Note that (a=b) - (c=d) yields a - c = b - d. Note also that I've put in another step (in %i7) to apply the diff operator. Also I've multiplied %o7 by r to get something like base.
(%i1) depends (\rho,[t, r, \theta, z]); depends (V, [t, r, \theta, z]);
(%o1) [rho(t, r, theta, z)]
(%o2) [V(t, r, theta, z)]
(%i3) f_contin : diff (\rho, t) + diff (\rho*r*V[r], r)*(1/r) = 0;
drho d
r V ---- + r (-- (V )) rho + V rho
drho r dr dr r r
(%o3) ---- + ------------------------------------ = 0
dt r
(%i4) base : diff (V[b]*r*\rho, r) = 0;
drho d
(%o4) V r ---- + (-- (V )) r rho + V rho = 0
b dr dr b b
(%i5) V_sub : V[r] = V[b] + \epsilon*V[r];
(%o5) V = epsilon V + V
r r b
(%i6) subst (V_sub, f_contin);
drho drho d
(%o6) ---- + (r (epsilon V + V ) ---- + r (-- (epsilon V + V )) rho
dt r b dr dr r b
+ (epsilon V + V ) rho)/r = 0
r b
(%i7) %o6, nouns;
drho drho d d
(%o7) ---- + (r (epsilon V + V ) ---- + r (epsilon (-- (V )) + -- (V )) rho
dt r b dr dr r dr b
+ (epsilon V + V ) rho)/r = 0
r b
(%i8) expand (r*%o7 - base);
drho drho d
(%o8) r ---- + epsilon r V ---- + epsilon r (-- (V )) rho + epsilon V rho = 0
dt r dr dr r r
The function subst (a,b,c) substitutes a for b in c. It uses 3 arguments, your first subst works because its interpreted as subst (V[b] + \epsilon*V[r],V[r], f_contin);
Your second subst is probably interpreted as subst (0,diff (V[b]*r*\rho, r),%) therefor nothing is substituted. What do you want to substitute for what?
The problem generally appears if I have a class containing, for example, a couple of slots that would be filled with vectors. If I want to make the object of this class more-or-less transparent, I implement print-object for it. And here I am faced with the problem:
If I print everything in one line, REPL's heuristics are not good enough to determine how to arrange printable parts in multiple lines, causing everything to be shifted to the right (see example below).
If I decide to split the output into multiple lines manually, I have a problem of how to indent everything properly, such that if this object is a part of another object, indentation is preserved (see example below for more clarity).
Here is the code. Consider two classes:
(defclass foo ()
((slot1 :initarg :slot1)
(slot2 :initarg :slot2)))
(defclass bar ()
((foo-slot :initarg :foo)))
And I have the following instances:
(defparameter *foo*
(make-instance 'foo
:slot1 '(a b c d e f g h i j k l m n o p q r s t u v)
:slot2 #(1 2 3 4 5 6 7 8)))
(defparameter *bar*
(make-instance 'bar
:foo *foo*))
What I want to see, is something like this:
> *bar*
#<BAR
foo-slot = #<FOO
slot1 = (A B C D E F G H I J K L M N O P Q R S T U V)
slot2 = #(1 2 3 4 5 6 7 8)>>
Case 1: Printing everything in one line
Definitions of print-object for these classes can be something like these:
(defmethod print-object ((obj foo) out)
(with-slots (slot1 slot2) obj
(print-unreadable-object (obj out :type t)
(format out "slot1 = ~A slot2 = ~A" slot1 slot2))))
(defmethod print-object ((obj bar) out)
(with-slots (foo-slot) obj
(print-unreadable-object (obj out :type t)
(format out "foo-slot = ~A" foo-slot))))
However, their printable representation is less than ideal:
> *foo*
#<FOO slot1 = (A B C D E F G H I J K L M N O P Q R S T U V) slot2 = #(1 2 3 4 5
6 7 8)>
> *bar*
#<BAR foo-slot = #<FOO slot1 = (A B C D E F G H I J K L M N O P Q R S T U V) slot2 = #(1
2
3
4
5
6
7
8)>>
Case 2: Attempt to multiple line printing
Using multiple line printing, I don't know how to control indentation:
(defmethod print-object ((obj foo) out)
(with-slots (slot1 slot2) obj
(print-unreadable-object (obj out :type t)
(format out "~%~Tslot1 = ~A~%~Tslot2 = ~A" slot1 slot2))))
(defmethod print-object ((obj bar) out)
(with-slots (foo-slot) obj
(print-unreadable-object (obj out :type t)
(format out "~%~Tfoo-slot = ~A" foo-slot))))
Thus, *foo* prints OK, but *bar* isn't:
> *foo*
#<FOO
slot1 = (A B C D E F G H I J K L M N O P Q R S T U V)
slot2 = #(1 2 3 4 5 6 7 8)>
*bar*
#<BAR
foo-slot = #<FOO
slot1 = (A B C D E F G H I J K L M N O P Q R S T U V)
slot2 = #(1 2 3 4 5 6 7 8)>>
In the past I tried to play around with print-indent, but without a success (I couldn't see any effect from it, maybe wasn't using it correctly, SBCL 1.2.14).
Is there a (preferably simple) way to solve this?
Try something like this (might require more polishing):
(defmethod print-object ((obj foo) out)
(with-slots (slot1 slot2) obj
(print-unreadable-object (obj out :type t)
(format out "~<~:_slot1 = ~A ~:_slot2 = ~A~:>" (list slot1 slot2)))))
(defmethod print-object ((obj bar) out)
(with-slots (foo-slot) obj
(print-unreadable-object (obj out :type t)
(format out "~<~:_foo-slot = ~A~:>" (list foo-slot)))))
It uses ~< and ~:>, which are format operations for logical blocks. Then it uses ~:_, which is a conditional newline. You should read the relevant hyperspec section.