Spad is a language for the Axiom/FriCAS symbolic computing mathematics program and I have put some information about it on this page.
Here are some issues that I came across while trying to debug SPAD, I've no idea if this is the best way to go about debugging, perhaps the experts will give me some hints?
SPAD Jargon
SPAD has a steep learning curve, this is not helped by obscure jargon, for example, when trying to interpret error messages it may help to try the following translations:
SPAD Jargon | Translation |
---|---|
mode | type |
modemap | type signature |
NoValueMode | category used when domain expected |
mode $ | type % |
Trace Command
This is the main method that I have found for debugging is the trace command, this shows the parameters when a function is entered and the return value when the function is exited:
(5) -> )trace GRAS )math Packages traced: GrassmannAlgebra(3,Fraction(Integer),[[1,0,0],[0,1,0],[0,0, 1]]) Parameterized constructors traced: GRAS (5) -> lc(a,b/\c) 1<enter GrassmannAlgebra./\,56 : arg1= e 2 arg2= e 3 1>exit GrassmannAlgebra./\,56 : e e 2 3 1<enter GrassmannAlgebra.lc,60 : arg1= e 1 arg2= e e 2 3 1<enter GrassmannAlgebra.addLcProd,74 : arg1= 1 arg2= 1 arg3= 1 arg4= 6 arg5= 0 2<enter GrassmannAlgebra.addLcProd,74 : arg1= 1 arg2= 1 arg3= 1 arg4= 1 arg5= 0 2>exit GrassmannAlgebra.addLcProd,74 : 1 1 |
In the above trace command I used the )math option. If this is missed out I get what looks to me like lisp stuff which I can't read:
1<enter GrassmannAlgebra./\,56 : #(#1=(0 . 1) #1# (1 . 1) #1# #1# #1# #1# #1#)\#(#1=(0 . 1) #1# #1# #1# (1 . 1) #1# #1# #1#) 1> |
I have only been able to trace external functions, if I want to trace 'local' functions then I have temporarily declared them as external while debugging.
Print/Output
What I would like to do is to print out values and strings during debugging, possibly between the trace messages. To do this use:
sayTeX$Lisp "the value of name is: "name
Warnings
The most common warnings that I get when compiling are:
- ?: ? has no value
- The variable ? is defined but never used
- undefined variable
The program seems to work despite these warnings. I think that some of them might be caused by 'global variables' like these:
Impl ==> add <snip>... x, y, z: % ++ working values for operations c: K ++ field multiplier m: Integer ++ integer multiplier |
?: ? has no value
Warnings:
[1] addMonomProd: c has no value
[2] addExteriorProd: c has no value
[3] grade: gr has no value
[4] /\: z has no value
[5] \/: z has no value
[6] addLcProd: resul has no value
[7] lc: z has no value
The variable ? is defined but never used
; file: /home/martin/GRAS.NRLIB/GRAS.lsp
; in: DEFUN |GRAS;leftMostBase|
; (DEFUN BOOT::|GRAS;leftMostBase| (BOOT::|b| BOOT::$)
; (PROG (#:G882 #:G883 BOOT::|i| BOOT::|mask|)
; (RETURN (VMLISP:SEQ (VMLISP:EXIT #) #:G882 (VMLISP:EXIT #:G882)))))
; --> PROGN EVAL-WHEN
; ==>
; (SB-IMPL::%DEFUN 'BOOT::|GRAS;leftMostBase|
; (SB-INT:NAMED-LAMBDA BOOT::|GRAS;leftMostBase|
; (BOOT::|b| BOOT::$)
; (BLOCK BOOT::|GRAS;leftMostBase|
; (PROG (#:G882 #:G883 BOOT::|i|
; BOOT::|mask|)
; (RETURN #))))
; NIL 'NIL (SB-C:SOURCE-LOCATION))
;
; caught STYLE-WARNING:
; The variable |b| is defined but never used.
undefined variable
; file: /home/martin/GRAS.NRLIB/GRAS.lsp
; in: DEFUN |GrassmannAlgebra;|
; (BOOT::|haddProp| BOOT::|$ConstructorCache| 'BOOT::|GrassmannAlgebra|
; (LIST BOOT::DV$1 BOOT::DV$2 BOOT::DV$3) (CONS 1 BOOT::$))
;
; caught WARNING:
; undefined variable: |$ConstructorCache|
;
warning message | reason |
---|---|
; caught WARNING: ; undefined variable: |$ConstructorCache| |
|
; caught WARNING: ; This variable is undefined: ; |$ConstructorCache| |
|
Error messages
I can't find a list of error massages.
error message | reason |
---|---|
>> System error: The value |Vector| is not of type LIST. |
Try specifing the full type that is: Also see Waldek s reply to my message below. Test case: )abbrev category ERR2 Error2 Error2: Category == Integer with myFn:(Boolean) -> Boolean |
>> System error: The index 2 is too large. |
I have seen this caused when attempting to extend some category or domain in an incompatible way. For example when attempting to create a domain which extends a category. Also see Waldek s reply to my message below. Test case: )abbrev category ERR1 Error1 Error1: Category == Integer with myFn:(NNI) -> Boolean |
>> Apparent user error: Cannot coerce 10 of mode (PositiveInteger) to mode $ |
|
>> Apparent user error: Cannot coerce b of mode (List (PositiveInteger)) to mode (NonNegativeInteger) |
|
>> Apparent user error: Operation 10 missing from domain: $ |
|
>> Apparent user error: unspecified error |
|
******** Boot Syntax Error detected ******** |
I used: e(i,j) == e(i) /\ e(j) when I should have used: e(i,j) == e(i) _/_\ e(j) |
>> Apparent user error: no modemap for /\ with 1 arguments |
e(i,j) == e(i) _/_\ e(j) |
>> Apparent user error: |
|
>> System error: example: PI ==> PositiveInteger
|
Can't have list of categories? |
>> Apparent user error: PI ==> PositiveInteger |
|
>> Apparent user error: NoValueMode is an unknown mode |
in import was needed for a domain that I was using |
>> Apparent user error: render is local and exported |
declaration and definition of render contained a parameter type that was not yet defined. |
>> Error detected within library code: |
this happened when I used PrimativeArray instead of List |
Discussion
Here is a message I put on the FriCAS developers list:
Since compiler error messages have been mentioned I was wondering what is your approach to improving error messages? I seem to remember Waldek saying that the only way to improve the poor error messages is to fix them on a case-by-case basis? If so, do you want unhelpful error messages reported here? For instance messages like this are not very helpful: >> System error: The index 2 is too large. I have seen this caused when attempting to extend some category or domain in an incompatible way. For instance, several times I have unintentionally used the name of an existing library category when I was intending to create a new name. This sort of error is easy to make so helpful error message would improve things. I have reconstructed this type of error in the 3 examples below. Just to be clear, I know these are errors, its just the unhelpfulness of the message that I'm talking about. Martin ---------------------------------------------------------------- Example 1: gives: The index 2 is too large. ---------------------------------------------------------------- )abbrev category ERR1 Error1 Error1: Category == Integer with myFn:(NNI) -> Boolean ---------------------------------------------------------------- Example 2: gives: The value #( |
Here is the reply from Waldek Hebisch:
Martin Baker wrote: > > Since compiler error messages have been mentioned I was wondering what > is your approach to improving error messages? > I seem to remember Waldek saying that the only way to improve the poor > error messages is to fix them on a case-by-case basis? > If so, do you want unhelpful error messages reported here? > > For instance messages like this are not very helpful: > > >> System error: > The index 2 is too large. > > I have seen this caused when attempting to extend some category or > domain in an incompatible way. > For instance, several times I have unintentionally used the name of an > existing library category when I was intending to create a new name. > This sort of error is easy to make so helpful error message would > improve things. It is not easy to get better error message. More precisely my approach in such cases is to do: )set break break and then trigger problem again and look at the backtrace. Usually this is more informative than any error message could get. Drawback is that you need to look at source code to find out what really happended. In case like you mention above, the problem is that we get inconsistent data structures, as parts of code use old definition while other parts use new one. Easy way to avoid such errors would be to forbid redefinition of domains and categories. But that would force _very_ inefficient developement style, basically requireing to recompile everthing before each test. Similarely, trying to ensure that various data structures are consistent requires nontrivial work. ATM the only implemented way to do full recompilation. In principle we could do such check much faster, but the effort is to implement faster version is comparable with rewritnig the compiler. Now, when we have inconsitent data structures, there is almost nothing we can do to get better error messages, as diagnosing errors depends on information from data structures. OTOH while I did get sometimes errors due to inconsitent domains/categories for me they tends to be rare and easy to fix: basically when I see weird error it is moment to think if everyting that should be recompiled in fact is. I do not remember the last time such error happened to me, but I think it was at least few month ago. When I see 'The index 2 is too large' error it usually what it means: out of bound array access in my code. > I have reconstructed this type of error in the 3 examples below. > > Just to be clear, I know these are errors, its just the unhelpfulness of > the message that I'm talking about. > > Martin > > ---------------------------------------------------------------- > Example 1: gives: The index 2 is too large. > ---------------------------------------------------------------- > )abbrev category ERR1 Error1 > Error1: Category == Integer with > myFn:(NNI) -> Boolean > ---------------------------------------------------------------- > Example 2: gives: The value #( |
With last commit compiler now catches the error. For the first two the message is: Semantic Errors: [1] cannot form Join of: ((Integer) (CATEGORY package (SIGNATURE myFn ((Boolean) NNI)))) ****** comp fails at level 2 with expression: ****** (|Join| | << | (|Integer|) | >> | (CATEGORY |package| (SIGNATURE |myFn| ((|Boolean|) NNI)))) ****** level 2 ****** $x:= (Integer) $m:= (Category) $f:= (((($ #) (|Error1| #) (|$DomainsInScope| # #)))) >> Apparent user error: cannot compile (Integer) Still not nice, but to the point: compiler now complains that it can not compile 'Integer' to give it type 'Category', which is exactly the error. For the third we get: cannot produce category object: (|Join| (|Integer|) (CATEGORY |package| (SIGNATURE |myFn| ((|Boolean|) (|Boolean|))))) >> Apparent user error: cannot produce category object which is less direct, but still says that there something wrong with categories. Note: Both example print 'Join', while there is no explicit 'Join' in the program. However, ..... Integer with myFn:(Boolean) -> Boolean is transformed to 'Join' before typechecking, and that is what compiler sees. Changing that is harder than the change I made. BTW. Please come with specific problems. General statements like 'error messages are bad' are hard to fix. In many cases specific problems can be fixed quickly. -- Waldek Hebisch |
Profiler
On 11/19/19 5:49 AM, Ralf Hemmecke wrote: >> To answer question "what/why takes time" use profiler. sbcl >> profiler shows that time is spent in factorizer. > > Thank you. I think that helps me to continue. > > Still I would be interested in how exactly I would have use the profiler > and how to interpret the output. > > I used what was suggested some time ago > > )lisp (require :sb-sprof) > )lisp (sb-sprof:start-profiling) > -- fricas computation here > coerce(c2)$F4 > )lisp (sb-sprof:stop-profiling) > )lisp (sb-sprof:report) > > Ralf |
The "Count" you see is not the number of times a certain function is called, instead, the calling stack is interrupted and examined periodically to see which function is being called: so this is a statistical method to get how much time each function is spending -- the time spent by a function is proportional to the count it gets during those examination. Also notice the difference between "Count" and "Total Count", "Count" means how much time is spent by function body, "Total Count" means how much time is spent by function body and sub-function calls. -- oldk1331 |
Counts give estimate for time. Nice feature of sbcl profiler is that it gives you also estimate for time spent in functions called from given function. One needs to look for times that are suspicously high, in this case: 10654 98.1 |FFPOLY;createIrreduciblePoly;PiSup;16| [364] 25 0.2 10654 98.1 |FFPOLY;nextIrreduciblePoly;SupU;11| [62] 1 0.0 |DDFACT;ddffact1| [39] 1 0.0 SB-KERNEL::INTEXP [75] 6 0.1 |LIST;setfirst!;$2S;12| [101] 2 0.0 |FFP;index;Pi$;17| [137] 4 0.0 TRUNCATE [11] 11 0.1 |FFPOLY;revListToSUP| [99] 200 1.8 |SAE;index;Pi$;35| [56] 10399 95.7 |DDFACT;irreducible?;FPB;8| [94] Indented line ('nextIrreduciblePoly') is function under consideration. Before is infor about callers, in this case 'createIrreduciblePoly'. After is info about called functions, clearly 'irreducible?' dominates here. You can see that also 'index' takes some time. -- Waldek Hebisch |