a custom recursively defined description of objects

32 views
Skip to first unread message

Martin R

unread,
Apr 1, 2026, 5:53:28 AM (5 days ago) Apr 1
to FriCAS - computer algebra system
Dear all!

Developing an idea of Waldek, I came up with the design and the code below to obtain a customizable description, somewhat similar to InputForm, that works recursively.  I want to use this to create a better interface translating FriCAS objects to SageMath objects.

I would greatly appreciate input and criticism of the design.

One principle it that all code is controlled by the customer wishing to translate FriCAS objects into her application, i.e., there are no changes to FriCAS itself

Then, given a FriCAS object of some type, the customer creates an appropriate package call to one of the packages below, depending on the customer's needs.  For example:

(1) -> l := [5::PF 7, 3, 1]

   (1)  [5, 3, 1]
                                                    Type: List(PrimeField(7))
(2) -> sexport(l)$SimpleExport(PF 7, FiniteExport PF 7)

   (2)  (5 3 1)
                                                            Type: SExpression

Note that List PrimeField 7 does have InputForm, but I choose a different export format.  Put differently, the packages below are just tools that enable me to create the SExpression I want.

My main question is about the extensibility, and whether there are any obvious improvements I am missing.  In particular, I would rather not like to create too many packages similar to the ones below.  However, for example, to handle various polynomial constructors, I will at least need something like

)abb package EXPORT3 TripleExport
TripleExport(A : Type, B : Type, T : Type, ET : with (sexport : T -> SExpression)) : with
        sexport: A -> SExpression
    == add
        if T has SetCategory and B has SetCategory and A has IndexedDirectProductCategory(T, B) then
            sexport(obj: A): SExpression ==
                l := listOfTerms obj
                ...

Does this sound reasonable?

Best wishes,

Martin


)abb package EXPORTI InputFormExport
++ For domains whose InputForm can be used without change, such as
++ INT, PI, EXPR INT.  Do not use for constructors such as LIST, FRAC or
++ POLY.
InputFormExport(R: ConvertibleTo InputForm) : with
        sexport: R -> SExpression
    == add
        sexport(obj: R): SExpression ==
            convert(convert(obj)@InputForm)@SExpression

)abb package EXPORTF FiniteExport
++ For domains like PF and FF.
FiniteExport(R: Finite) : with
        sexport: R -> SExpression
    == add
        sexport(obj: R): SExpression ==
            convert(lookup(obj))@SExpression

)abb package EXPORT1 SimpleExport
++ For domain constructors depending only on a single type
SimpleExport(T : Type, ET : with (sexport : T -> SExpression)) : with
        sexport: List T -> SExpression
        sexport: Fraction T -> SExpression
        sexport: Factored T -> SExpression
        sexport: Matrix T -> SExpression
        sexport: Polynomial T -> SExpression
    == add
        sexport(obj: List T): SExpression ==
            convert([sexport(e)$ET for e in obj])@SExpression

        if T has IntegralDomain then
            sexport(obj: Fraction T): SExpression ==
                num := sexport(numer obj)$ET
                den := sexport(denom obj)$ET
                convert([num, den])@SExpression

            sexport(obj: Factored T): SExpression ==
                u := sexport(unit obj)$ET
                l := [convert([sexport(f.factor)$ET,
                               convert(f.exponent)@SExpression])@SExpression
                      for f in factors obj]
                convert([u, convert(l)@SExpression])@SExpression

        if T has AbelianMonoid then
            sexport(obj: Matrix T): SExpression ==
                convert([convert([sexport(e)$ET for e in row])@SExpression
                         for row in listOfLists obj])@SExpression

        if T has Ring then
            monomial(ie: IndexedExponents Symbol): SExpression ==
        l: List SExpression := [convert([convert("^"::Symbol)@SExpression,
                                                 convert(t.k)@SExpression,
                                                 convert((t.c)::Integer)@SExpression])@SExpression
                                        for t in listOfTerms(ie)]
(# l = 0) => convert(1::Integer)@SExpression
(# l = 1) => l.1
                convert(concat([convert("*"::Symbol)@SExpression], l))@SExpression


            sexport(obj: Polynomial T): SExpression ==
                l := listOfTerms obj
                convert([convert([monomial(mon.k), sexport(mon.c)$ET])@SExpression
         for mon in l])@SExpression

Waldek Hebisch

unread,
Apr 2, 2026, 12:06:42 AM (4 days ago) Apr 2
to 'Martin R' via FriCAS - computer algebra system
It is not clear to me what you really want. In particular, for
about 18 years Sage interface was using InputForm. What trouble
you want to avoid by using different representation?

Note that design like InputForm or OutputForm work by cooperation
of "all" FriCAS domains. Each involved domain must implement its
part. Normal FriCAS policy is to hide internal details and
implementing things like InputForm or OutputForm usually is
easier with access to internals (otherwise extracting needed
information can be tricky). So, doing another form in similar
spirit, but from "outside" looks like a lot of work if you
want wide coverage. If you want to cover just some special
domains, then I would look first at improving InputForm.

AFAICS one issue during interfacing is to transmit raw values. That
could be done recursing over Lisp representation of data, similar
to what recently added FileSerialization (or old Boot code) is
doing. FriCAS uses limited number of Lisp types, basicaly integers,
symbols, cons cells, and various arrays (beside general 1D and 2D
arrays we also use specialized ones). Kernels use special
Lisp structure. One can use Lisp printer to get textual form
of such data or possibly use some binary form for easier
parsing and better efficiency (but at the cost of debugging
trouble).

Once you got raw data you need to make some sense of it. This
requires knowledge of FriCAS types. One potentially nice
approach is to build collection of proxy Python objects which
simply pass data that they own to FriCAS so that FriCAS can do
required computation. In principle creating such proxy object
could be done with process similar to build of Aldor interface,
that is by iterating over FriCAS constructors, retriving
information about constructors and using it to create a
proxy. I do not know how much effort is needed to create
usable interface of this sort.

BTW: In the past among other I looked at OpenMath standard.
AFAICS it resolves issue to transpoting raw values by specifying
appropriate protocol. But beyond that there is trouble: basically
for each kind of thing to transmit one need agreement between
both ends. For this OpenMath have so called "dictionaries".
There are some standard dictionaries. What I saw was
essentially trivial, that is number and a bunch of classic
functions on them. My impression was that each dictionary
was essentially build in ad hoc way by enumeration of
coverd constructs. For me that looked extremaly laborious
and unscalable. For that reason currenly OpenMath code in
FriCAS is non-functional. But if there are some useful dictionaries,
then adding support for them do not look hard assuming
reasonable scale (dictionary defining 1000 things would
require some work due to bulk, 10000 things would be
challenging due to needed manpower).

--
Waldek Hebisch

Martin R

unread,
Apr 2, 2026, 6:19:30 AM (4 days ago) Apr 2
to FriCAS - computer algebra system
Dear Waldek!

Thank you for taking the time!

> It is not clear to me what you really want. In particular, for about 18 years Sage interface was using InputForm. What trouble you want to avoid by using different representation?

18 years, wow! Indeed, I am an old man now :-(

The trouble with InputForm is threefold:
1) there are several domains that do not have ConvertibleTo InputForm
2) the InputForm of some is very verbose (which is problematic, since the interface transmits strings) and needs parsing
3) the InputForm of others is ambiguous

Details:
1) e.g., UnivariatePolynomial, DirectProduct, Record, Union

2) e.g. FiniteField and Factored

                                                      Type: Factored(Integer)
(10) -> factor(-42*1783)::INFORM

   (10)
   (*  - 1

     (*  (primeFactor (:: 2 (Integer)) 1)

       (*  (primeFactor (:: 3 (Integer)) 1)
        (* (primeFactor (:: 7 (Integer)) 1) (primeFactor (:: 1783 (Integer)) 1))
         )
       )
     )
                                                              Type: InputForm

3) e.g., MultivariatePolynomial

This does not return a list of terms, but rather an expression tree, which makes it very hard, if not impossible, to distinguish between coefficients and monomials:

(1) -> M ==> MultivariatePolynomial([x], POLY INT)
                                                                   Type: Void
(2) -> V ==> OrderedVariableList([x])
                                                                   Type: Void
(3) -> m := monomial(1, x::V)$IndexedExponents V

   (3)  x
                             Type: IndexedExponents(OrderedVariableList([x]))
(4) -> monomial(2*x, m)$M::INFORM

   (4)  (* (* 2 x) x)
                                                              Type: InputForm

Best wishes,

Martin

Waldek Hebisch

unread,
Apr 4, 2026, 12:06:09 PM (yesterday) Apr 4
to 'Martin R' via FriCAS - computer algebra system
On Thu, Apr 02, 2026 at 03:19:29AM -0700, 'Martin R' via FriCAS - computer algebra system wrote:
> Dear Waldek!
>
> Thank you for taking the time!
>
> > It is not clear to me what you really want. In particular, for about 18
> years Sage interface was using InputForm. What trouble you want to avoid by
> using different representation?
>
> 18 years, wow! Indeed, I am an old man now :-(
>
> The trouble with InputForm is threefold:
> 1) there are several domains that do not have ConvertibleTo InputForm
> 2) the InputForm of some is very verbose (which is problematic, since the
> interface transmits strings) and needs parsing
> 3) the InputForm of others is ambiguous
>
> Details:
> 1) e.g., UnivariatePolynomial, DirectProduct, Record, Union

For UnivariatePolynomial we in principle could produce the
essentially the same InputForm as for multivariate one.
Depending what we value more (ability to change type or round
trip fidelty) we could add coercion to the right type.
For DirectProduct we could just use input form for Vector as
argument to directProduct (and probably add dolar qualification
to allow simple evaluation in the interpreter).

Similarly we could addd InputForm for Record-s and Union-s.

SparseUnivariatePolynomial is a bit more problematic, normally
I create element using univairate, but this needs free variable.
We could use 'monomial' to create terms, but this is more
verbose.

> 2) e.g. FiniteField and Factored
>
> Type:
> Factored(Integer)
> (10) -> factor(-42*1783)::INFORM
>
> (10)
> (* - 1
>
> (* (primeFactor (:: 2 (Integer)) 1)
>
> (* (primeFactor (:: 3 (Integer)) 1)
> (* (primeFactor (:: 7 (Integer)) 1) (primeFactor (:: 1783
> (Integer)) 1))
> )
> )
> )
> Type:
> InputForm

I see no cure for this within current InputForm.

> 3) e.g., MultivariatePolynomial
>
> This does not return a list of terms, but rather an expression tree, which
> makes it very hard, if not impossible, to distinguish between coefficients
> and monomials:
>
> (1) -> M ==> MultivariatePolynomial([x], POLY INT)
> Type:
> Void
> (2) -> V ==> OrderedVariableList([x])
> Type:
> Void
> (3) -> m := monomial(1, x::V)$IndexedExponents V
>
> (3) x
> Type:
> IndexedExponents(OrderedVariableList([x]))
> (4) -> monomial(2*x, m)$M::INFORM
>
> (4) (* (* 2 x) x)
> Type:
> InputForm

Yes, this is a problem. I do not see how to solve this without
creating much more verbose InputForm.

In this example going via Lisp representation looks easier:

(21) -> PRETTYPRINT(serialize(monomial(2*x, m)$M::None)$FileSerialization)$Lisp
(1 1 (1 0 1 |x| (1 0 . 2)))

Note: above 'serialize' is doing essentially nothing, but I added it
because it is needed for other types.


--
Waldek Hebisch

Ralf Hemmecke

unread,
Apr 4, 2026, 2:58:40 PM (yesterday) Apr 4
to fricas...@googlegroups.com
Martin,

just a question. Why do you need InputForm? Isn't InputForm a format so
that **FriCAS** can easily read/interpret data? As I understand you want
SAGE to interpret data from FriCAS, right?

Do you know about Format1D? This is certainly not a soluton for
everything, since it goes via OutputForm (and that loses information),
but maybe it is easy for SAGE to interpret.

This formatting framework is also flexible enough to adjust the output.

Note that there also is a FormatMathematica, which is on the same line
of what I suggest above.

https://groups.google.com/g/fricas-devel/c/zG9C7QnztQ0/m/r6TtVVOZAQAJ
https://github.com/hemmecke/fricas/tree/wip/fmtmma

Ralf

Martin R

unread,
11:25 AM (9 hours ago) 11:25 AM
to FriCAS - computer algebra system
Thank you all for your replies!

@Ralf, the point of the translation is that types are preserved as far as possible.  There are minor differences, but with the approach indicated above, the following works well:

sage: f = fricas("matrix([[((i+j)*x^i/(1+y^j))::FRAC MPOLY([x,y], PF 3) for i in 1..5] for j in 1..5])")
sage: f.sage()
[    (-x)/(y + 1)                0      x^3/(y + 1)   (-x^4)/(y + 1)                0]
[               0    x^2/(y^2 + 1) (-x^3)/(y^2 + 1)                0    x^5/(y^2 + 1)]
[     x/(y^3 + 1) (-x^2)/(y^3 + 1)                0    x^4/(y^3 + 1) (-x^5)/(y^3 + 1)]
[  (-x)/(y^4 + 1)                0    x^3/(y^4 + 1) (-x^4)/(y^4 + 1)                0]
[               0    x^2/(y^5 + 1) (-x^3)/(y^5 + 1)                0    x^5/(y^5 + 1)]
sage: _.parent()
Full MatrixSpace of 5 by 5 dense matrices over Fraction Field of Multivariate Polynomial Ring in x, y over Finite Field of size 3

Since I got everything working now, I think that this approach is not so bad!


Best wishes,

Martin

Martin R

unread,
11:27 AM (9 hours ago) 11:27 AM
to FriCAS - computer algebra system
NB: what's completely missing is a sensible way to transmit sage data to FrICAS.

Martin R

unread,
3:25 PM (5 hours ago) 3:25 PM
to FriCAS - computer algebra system
I just realized that I am having a problem with Records (and possibly also Unions)!

Can I somehow convert a Record to something like List Any?

Best wishes,

Martin

Waldek Hebisch

unread,
3:39 PM (4 hours ago) 3:39 PM
to 'Martin R' via FriCAS - computer algebra system
On Sun, Apr 05, 2026 at 12:25:51PM -0700, 'Martin R' via FriCAS - computer algebra system wrote:
> I just realized that I am having a problem with Records (and possibly also
> Unions)!
>
> Can I somehow convert a Record to something like List Any?

I do not think this is possible using well typed high level Spad
operation. You can do this using low level operations:
- two element record is just a Lisp cons cell
- record with more than 2 elements is a vector, so you can
pretend it to Vector(None)

You need the type to get from None to Any.

--
Waldek Hebisch
Reply all
Reply to author
Forward
0 new messages