Skip to content

Support reasoning over most the SMT-LIB FPA theory symbols - #1364

Open
hra687261 wants to merge 32 commits into
OCamlPro:nextfrom
hra687261:fpa_axioms
Open

Support reasoning over most the SMT-LIB FPA theory symbols#1364
hra687261 wants to merge 32 commits into
OCamlPro:nextfrom
hra687261:fpa_axioms

Conversation

@hra687261

@hra687261 hra687261 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

This PR adds a prelude which axiomatizes the semantics of most of the SMT-LIB FPA theory symbols, and maps the symbols of the theory parsed by dolmen to these axiomatizations, providing support for symbolic reasoning over most of them (except fp.rem, fp.to_fp, fp.of_sbv, fp.of_ubv, fp.to_ubv and fp.to_sbv the support for which will be added in the future in a separate PR).
The PR also propagates for each of the FPA literals for which support was added in #1358 predicates and assertions which allow linking these literals to the axiomatization. It also adds propagation of equalities with literals from asserted predicates, as well as a shostak theory to solve equalities between fp literals among themselves or fp literals and fp variables.

@hra687261
hra687261 marked this pull request as draft June 24, 2026 14:06
@hra687261
hra687261 force-pushed the fpa_axioms branch 2 times, most recently from f64ec20 to 0b6592b Compare June 30, 2026 14:24
@hra687261
hra687261 marked this pull request as ready for review June 30, 2026 14:27
hra687261 added 2 commits July 7, 2026 23:36
…ways be literals in the theory, remove support for symbolic arguments for ae.round + float*
Comment thread src/lib/structures/expr.ml Outdated
Comment thread src/lib/structures/expr.mli Outdated
Comment thread src/lib/structures/fp_value.ml Outdated
| NaN -> Fmt.pf ppf "NaN"
| Finite { neg; biased_exp; significand } ->
Fmt.pf ppf "fp[%b;%d;%s]" neg biased_exp (Z.to_string significand)
| Finite q -> Fmt.pf ppf "fp[%s]" (Q.to_string q)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We display the size of the biased and significand in the smtlib printer but loss it in the debug printer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, because the smt-lib printer is expected (I suppose) to produce something that is (at least close) to something parseable in the smt-lib format, while this one is for debugging only.
On the other hand, I am not sure if for debugging it is better to print a Q.t or the mantissa/significand bit-vectors, intuitively, since we represent finite Floats as Q.t, I'd say its this one but I am not sure, in practice I've seen that it quickly becomes unreadable anyway.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally for debug we would print as a float, i.e. a real with a dot (decimal would be nice but hexadecimal is easy to do I believe)!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could simply convert to float then print with "%.17g", but we would lose precision on float > 64, so maybe the hex format is best.
The current version should be good.

Comment thread src/lib/util/theories.ml Outdated
Comment thread src/preludes/smt-lib-fpa.smt2
Comment thread src/lib/reasoners/fpa_rel.ml Outdated
Comment thread src/lib/reasoners/fpa_rel.ml Outdated
Comment thread src/lib/reasoners/fpa_rel.ml Outdated
watched_args = add_by_arg r_mode term t.watched_args
}

let subst ~ex:_ rr nrr t =

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to track the explanation as these domains cannot generate conflicts?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, this one simply tracks arguments and substitutes them when they becomes literals, then propagates equalities, but it might actually still be necessary to pass the explanation down to the deduced equalities, I will do that. (Not exactly sure, the explanation is probably attached to the semantic values already no?)
Now, that I think of it, since Sy.Op Float now supports symbolic arguments for the rounding mode, its not clear that this domain is still necessary.

Comment thread src/lib/reasoners/fpa_rel.ml Outdated
Comment thread src/lib/reasoners/fpa_rel.ml Outdated
@@ -0,0 +1,1845 @@
(set-logic ALL)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be concerned about the license of this file? It is essentially a one-one translation of the Why3 theory.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, though the transformation is pretty significant, I wouldn't call it a "one-one translation". It's also not clear how the license applies, since the original axiomatization was technically generated by Why3 and not part of its source code.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alt-Ergo is distributed under a unfree license but Why3 is a free software under the terms of LGPL 2.1 https://gitlab.inria.fr/why3/why3/-/blob/master/LICENSE?ref_type=heads

This file is a kind of derivative work of the original file in Why3.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is a kind of derivative work of the original file in Why3.

That is unclear, since:

the original axiomatization was technically generated by Why3 and not part of its source code.

(Though it is generated from a whyml module which is part of the Why3 source code ...)

Anyway, what do you suggest?

@bclement-ocp bclement-ocp left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks :) Did a first pass with some comments, but did not look at the prelude and some calculations in detail yet.

@@ Dolmen_type.Base.app0
(module Dl.Typer.T)
env s DE.Ty.roundingMode)
|> add_constrs )

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should remove the rounding mode type from fpa_rounding.ml if we don't use it anymore (and if we do, replace it with DE.Ty.roundingMode).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. But unfortunately, rounding modes in Dolmen are put in the same ADT as other FP symbols, so it makes matching on rounding modes awkward (we always have to skip the cases of the non-rounding mode symbols).
I will open a PR on Dolmen to put rounding modes in their own enum type (with the hope that it will be accepted and be part of the next Dolmen release).

Comment on lines +383 to +385
| Id { ns = Term; name = Simple "ae.float" } ->
Dl.Typer.T.builtin_term
@@ Dolmen_type.Base.term_app_cst (module Dl.Typer.T) env ae_float_cst

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, it is unfortunate that we need to expose both ae.round (with _ constant args) and ae.float :(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, IIRC indexed operators expect literals as indices not symbolic values, so we have to. But maybe in the future we can deprecate ae.round for ae.float, or rename ae.float to ae.round and make the precisions arguments normal arguments instead of indices (because its a better name, the operator in alt-ergo should probably renamed to something like FloatRound instead of just Float)

Comment thread src/lib/frontend/translate.ml Outdated
Comment on lines +464 to +465
| `App (`Builtin (B.Float B.Float.RoundingMode), []) ->
Fpa_rounding.fpa_rounding_mode

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should go above, along with the other App cases (likely just after B.Unit).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

when Compat.List.is_empty tyl && String.equal name E.FP.Names.t ->
(* To get the generic abstract float type from the axiomatization which is
stored as type varialbe `ae.fp.t`. *)
tv

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand this. Why don't we define ae.fp.t directly here (like we do e.g. for rounding mode) and make it available to the axiomatization?

Comment thread src/lib/frontend/translate.ml Outdated
Comment on lines +807 to +811
| B.Float RoundNearestTiesToEven -> mk_rounding NearestTiesToEven
| B.Float RoundNearestTiesToAway -> mk_rounding NearestTiesToAway
| B.Float RoundTowardPositive -> mk_rounding Up
| B.Float RoundTowardNegative -> mk_rounding Down
| B.Float RoundTowardZero -> mk_rounding ToZero

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep these within the global match on B.Float below (moving guards on when get_smt_lib_fpa to the constants).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread src/lib/structures/expr.mli Outdated
Comment thread src/lib/structures/expr.mli Outdated
val ieee_format_to_fp : t -> int -> int -> t

(* arithmetic with rounding mode *)
val add : e:int -> s:int -> mode:t -> t -> t -> t

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The e and s labelled arguments should maybe be explained somewhere? Is there a reason they are called e and s rather than eb and sb following the SMT-LIB?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This just follows what the Dolmen constructors used, e and s probably as short for eb and sb.
I changed them.

Comment thread src/lib/structures/fp_value.ml Outdated
| NaN -> Fmt.pf ppf "NaN"
| Finite { neg; biased_exp; significand } ->
Fmt.pf ppf "fp[%b;%d;%s]" neg biased_exp (Z.to_string significand)
| Finite q -> Fmt.pf ppf "fp[%s]" (Q.to_string q)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally for debug we would print as a float, i.e. a real with a dot (decimal would be nice but hexadecimal is easy to do I believe)!

(* hidden_bit = 2^(sb-1), m >= hidden_bit -> normal *)
let hidden_bit = Z.shift_left Z.one (sb - 1) in
let biased_exp, significand =
if Z.compare m hidden_bit >= 0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can simply check Z.testbit m (sb - 1)?

Comment thread src/preludes/smt-lib-fpa.smt2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants