Debug SPAD Language

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:

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:
Vector DoubleFloat
instead of just Vector

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)
should have used
e(i,j) == _/_\(e(i),e(j))

>> Apparent user error:
cannot compile (reduce /\ (COLLECT (IN i l) (e i)) (Zero))

 

>> System error:
The value |SetCategory| is not of type LIST.

example:

PI ==> PositiveInteger
LPS ==> List Permutation SetCategory
PS ==> Permutation SetCategory
lookup(lps2:LPS,i:PI): PS == lps2.i


Can't have list of categories?

>> Apparent user error:
unspecified error

PI ==> PositiveInteger
LPSET ==> List Permutation Set
PSET ==> Permutation Set
lookup(lps2:LPSET,i:PI): PSET == lps2.i

 
>> 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:
Can have at most 9 scripts of each kind

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 #()is not of type LIST.
----------------------------------------------------------------
)abbrev category ERR2 Error2
Error2: Category == Integer with
    myFn:(Boolean) -> Boolean
----------------------------------------------------------------
Example 3: gives:  >> System error: The index 2 is too large.
----------------------------------------------------------------
)abbrev domain ERR3 Error3
Error3() : Exports == Impl where
    Exports ==> Integer with
     myFn : (Boolean) -> Boolean
    Impl ==> add
     myFn(inp:Boolean)==inp 

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 #()is not of type LIST.
> ----------------------------------------------------------------
> )abbrev category ERR2 Error2
> Error2: Category == Integer with
>      myFn:(Boolean) -> Boolean
> ----------------------------------------------------------------
> Example 3: gives:  >> System error: The index 2 is too large.
> ----------------------------------------------------------------
> )abbrev domain ERR3 Error3
> Error3() : Exports == Impl where
>      Exports ==> Integer with
>       myFn : (Boolean) -> Boolean
>      Impl ==> add
>       myFn(inp:Boolean)==inp

OK, that is the same plain error (using Integer as a category)
and compiler should catch it.
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

metadata block
see also:
Correspondence about this page

This site may have errors. Don't use for critical systems.

Copyright (c) 1998-2023 Martin John Baker - All rights reserved - privacy policy.