feat(semantics): evaluate KerML Rationals exactly - #838
Open
devin-ai-integration[bot] wants to merge 33 commits into
Open
devin-ai-integration[bot] wants to merge 33 commits into
devin-ai-integration[bot] wants to merge 33 commits into
Conversation
Represent Rational values as normalized exact fractions with a compact small-fraction path and a size budget, parse decimal literals exactly, and carry exact Rationals through runtime, solver replay, gRPC, JSON, the Go client and compiled calculations. Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…y-design Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ation Co-Authored-By: jason.han <hanhuijun@gmail.com>
…Node Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…n-free Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Contributor
Author
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
Contributor
Author
…ion and read exact Rational inputs A Rational meeting a binary64 Real is rounded once before comparison, as in mixed arithmetic, so a binding across a Real declaration stays equal. Clients send every exact Rational as rational_value under rational_values, the service reads a binary64-exact one as that Rational, and a realValue stays a Real. Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # client/node/src/core/document.ts
…ned 1.83.0 Co-Authored-By: jason.han <hanhuijun@gmail.com>
…n clients, hold RealFunctions quantity magnitudes as Reals Co-Authored-By: jason.han <hanhuijun@gmail.com>
… meets negative zero Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…s and products Co-Authored-By: jason.han <hanhuijun@gmail.com>
Compiled calcs keep exact Rational folding and refusals through Real-typed values that hold an Integer at run time; a Go program refuses such a run with the compile-time message. The new short-named for-loop fixture accumulates exact Rational literals, as the existing for-loop fixture does. Co-Authored-By: jason.han <hanhuijun@gmail.com>
An Integer to a negative literal power is classified as the exact quotient it is, so exact arithmetic over it is refused rather than rounded, and a Go quotient below the least Real or beyond the greatest fails as the interpreter does. Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # internal/workspace/libs/stdlib.snapshot
Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # docs/guide/09-clients.md # docs/reference/python-api.md
…API reference Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ser walkthroughs Co-Authored-By: jason.han <hanhuijun@gmail.com>
This was referenced Oct 5, 2026
Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # api/proto/sysml.pb.go # client/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/Sysml.java # client/julia/OpenSysML/src/OpenSysML.jl # client/node/src/generated/sysml_pb.ts # client/python/opensysml/connection.py # client/python/opensysml/proto/sysml_pb2.py # client/rust/conformance/sysml.descriptor.binpb # client/rust/opensysml/src/operations.rs # docs/reference/service-transports.md # internal/frontend/grpc/convert_big_integer_test.go # internal/frontend/grpc/service.go # internal/workspace/libs/stdlib.snapshot
…accumulation fixture's statements Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
A KerML
Rationalis now evaluated as a rational number instead of an IEEE binary64:0.1 + 0.2 == 0.3istrue,1 / 3is the Rational1/3,numer(rat(1, 3))is1.Realstays binary64.docs/project/exact-rational-evaluation.md, which earlier declined this, isrewritten as the record of the new decision; its pilot probes are kept as the documented
divergence from the pilot.
semantics.Valuegains an exactValRational: lowest terms, positive denominator, inline(
int64numerator /uint32denominator) when it fits, an immutablebig.Ratotherwise.Integers stay
ValInt; an Integer beyondint64is now abig.Ratover one.semantics.ParseRational); Integer/is the exactquotient (replaces the once-rounded
IntQuotient);+ - * /,**/^with an Integer exponent,comparisons,
rat,numer,denom,gcd,abs,floor,round,max,min,sum,product,ToString,ToRational,ToIntegerare exact.DefaultMaxIntegerBits(shared with Integers)is
semantics.ErrRationalSizeLimit, never a rounding; powers are refused from a lower boundbefore computing. A harmonic accumulation stops with that typed error.
0.3,2.0,1.5e-05); any other asnumer/denom(1/3). Values that were already binary fractions printunchanged in REPL, JSON and traces.
semantics.RealOf): a value written to aRealfeature (holdAsReal),RealFunctionsoperands, and a Rational meeting a binary64 Real in arithmetic are rounded to thenearest double once.
sqrt, trig,exp,ln, non-Integer**stay binary64.RealFunctions::sum/producthold every magnitude as a Real, an Integer quantity's included(
RealFunctions::sum((1 [m], 2 [m]))is3.0 [m]), as they already did for plain Integers.semantics.CompareReal):== != < <= > >=between a Rational and a binary64 Realcompare the Rational's nearest double with the Real, the same rounding mixed arithmetic does,
so a binding to a
Realfeature holds (x : Real = 0.1; x == 0.1andrat(1, 3) == 1.0 / 3.0are
true). A Rational past the binary64 range compares as ±Inf; NaN is unordered as before.Rational vs Rational stays exact, and so does Integer vs anything (
CompareIntReal). Evaluator,runtime, set membership, query equality, solver replay and
-compileshare the rule. AReal-typed accumulation therefore stays binary64 and never reaches the Rational budget.(
0.1 [m] + 1 [mm]is exactly0.101 [m]).Rationalmessage inValue.rational_value,Quantity.rational_magnitude,DocumentValue.rational_value, negotiated by a newrational_valuescapability exactly asbig_int_values. Answers are canonical: a binary64-exact Rational crosses asreal_value,any other as
rational_value. Inputs: a client sends every exact Rational asrational_valueto a service advertisingrational_values, and the service reads anylowest-terms
rational_value(a binary64-exact one included) as that exact Rational; aninbound
real_valueis always a Real. Toward an older service a client rewrites abinary64-exact Rational as
real_valueand refuses any other, naming the capability; aservice answering a client without the capability refuses (
UNIMPLEMENTED), never a nearestdouble. Non-lowest-terms encodings are rejected (
semantics.LowestTermsRational)."rationalValue": {"numerator": "1", "denominator": "3"}/"rationalMagnitude".opensysml.Rational, Pythonfractions.Fraction(generated classes typeRationalfeaturesFraction), Node{kind: "rational", numerator: bigint, denominator: bigint}, Java/Rust numerator–denominator pairs, JuliaRational{BigInt}, MATLAB decimal-stringstruct; each declares
rational_values. Client value equality (NodevaluesEqual, JavasameValue, Rustsame_value, Juliasame_value) follows the same comparison rule, so aset holding
1/3and1.0 / 3.0is refused as a duplicate.xsd:decimal; exponent literal →xsd:doubleonly when a double holdsit exactly, otherwise exact
xsd:decimal. Round trip is exact.part in is replayed and marked rounded (
Query.Rounded). Census over all fixtures and corpora:6 queries remain marked rounded, all genuinely inexact.
sysml -compile: exact constants are folded; a single operation over binary64-exact values iscompiled with guards; an Integer quotient compared with a whole number is compared exactly.
An Integer to a negative literal power is the exact quotient, rounded once where it reaches a
Real; one below the least Real fails as in the interpreter.
Still refused, and why:
sysml -compilerefuses — with a typed error naming the construct —a
Rationalparameter,a * 0.1over a variable,(a * 0.5) ** 2,a / 3 < 0.1,a ** -1 * 3, and collectionoperations over exact Rationals, because compiled code holds numbers as
int64/binary64 and thesewould need more than one rounding. A Go
Realparameter given an Integer meets an exact Rational only at run time, so there theprogram fails with the same message. Arithmetic over an infinity remains a typed error, as for
LiteralInfinitytoday.Two policy choices the specification does not settle, recorded as tool-defined in the
evaluation record:
above. Exact comparison would make
x : Real = 0.1; x == 0.1false and break the equality abinding asserts (KerML §7.4.9), since
holdAsRealrounds the bound value.rational_valueunderrational_values; an inboundreal_valueis always a Real; runtime semantics do not depend on the wire form.Specification basis
positive and negative infinity"; §9.3.2.2.9: Real "includes both rational and irrational numbers".
the KerML DataType Rational" — a finite decimal names a rational exactly.
RationalFunctions.kerml(§9.4.10) andIntegerFunctions.kerml(§9.4.11), vendored underinternal/workspace/libs: the declared result types decide classification — Integer/returnsRational, so6 / 3is the Rational2(istype Integerisfalse);floor,round,numer,denom,gcd, Integer+ - * % **returnInteger.RationalFunctions::'**'declaresy: Rational, but a non-Integer exponent has in general anirrational result, so only Integer exponents are exact; others are
RealFunctions::'**'.binary64 Real is at Real precision.
Realprecision, how a Rationalmeets a binary64 Real, the wire form of an input, print form. UML/fUML/PSSM say nothing about KerML Rationals.
docs/project/spec-compliance.md: new/updated rows for literals, arithmetic, representation,printing, the Real boundary, every wire boundary, RDF,
-compile,rat/numer/denom,floor/round, Integer/, solver replay and rounded marking.Pilot differs by design. The pinned pilot holds
LiteralRationalas a Javadouble. The tenprobes are committed as
tools/referee/exec/testdata/cases/exact_rationals.casesunder a newby-design: <clause>marker; five land in a newdiffers-by-designbucket with the clause besideboth raw outputs (
0.1 + 0.2 == 0.3,0.1 + 0.2 <= 0.3,0.3 < 0.1 + 0.2,(1.0 / 49.0) * 49.0 == 1.0, ten-fold0.1sum== 1.0), five agree within the harness'stwo-decimal tolerance. Referee run against
develop(446 cases) and this branch (456):agree 212→217 · differs-by-design 0→5, every other bucket unchanged (disagree 29,pilot-error 5,both-error 16, …); a per-case comparison of the two reports shows none of the446 existing cases moved bucket. The stale totals in
pilot-execution-referee.mdand thereferee skill are updated to the measured ones.
How it was verified
Four-layer tests: golden AST fixture; conformance
action_exact_rational_accumulation.sysml+.expected.json; golden traceaction_exact_rational_accumulation.trace.golden;robustness_exact_rational_test.go(
TestRuntimeRobustnessExactRational: denominator growth beyond a lowered budget, a power and aliteral beyond the size budget, division by zero — each a typed error);
robustness_rational_real_comparison_test.go(TestRuntimeRobustnessRationalRealComparison:Rational vs NaN, vs ±Inf, a Rational beyond the binary64 range vs a large Real, set membership,
and a 1000-step
Realaccumulation of0.01under a lowered budget). Conformanceinstance_real_binding_meets_rational(binding equality,rat(1,3) == 1.0/3.0,rat(1,4) == 0.25)and
action_real_accumulation_stays_binary64. gRPC conformance covers both wire directions(
evaluate_calc_rational_wire*,execute_action_rational_input_meets_rational,execute_action_real_input_meets_rational), as do the Python, Node, Julia, Rust, Java and MATLABclient tests.
-compilefixtures cover each comparison operator against the nearest double andrefuse a literal beyond the Real range.
Semantics unit tests cover storage, parsing, formatting, arithmetic, and wire validation.
training_examples_expected.txtis untouched; no corpus or RDF ratchet moved. Locally, GNU Octave6.4 lacks
jsonencode/jsondecode, so only the MATLAB encoding and capability tests ran there;the MATLAB/Octave CI job runs the full suite.
The browser REPL walkthroughs (
docs/assets/repl-walkthroughs.json, checked byTestBrowserREPLWalkthroughs) now expect100 [SI::km] / 2 [SI::h]as the exact125/9 [SI::'m/s'],%calc Speed 42.195 [SI::km] 2 [SI::h]as2813/480 [SI::'m/s'], and theRealFunctions::sumrollup
car.massas1300.0 [kg].Benchmarks (develop vs this branch, interleaved, six samples each,
benchstat):internal/exec/runtime, seven existingIntegerArithmeticLoopBeyondInt64tests/perf(REPLEvalExpr, ExecuteAction, BatchConstraints, SameConstraintManyInstances, BatchSatisfy, Instantiate, GRPCEvaluate)RationalDecimalLoop/RealDecimalLoopRationalHarmonicLoop(200 exact terms of 1/i)The byte increase beyond
int64is thebig.Rat-over-one representation; the harmonic loop is thegenuine cost of exactness (denominator of hundreds of bits), bounded by the size budget.
Checklist
make testandmake lintpass locallychanges/unreleased/<slug>.<section>.md, not as an edit toCHANGELOG.mdmake docs-countsrun if a gate count moved (compliance rows need nothing: the census is counted at docs build)F4,K5) in the body, docs, or changelogLink to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/0fe9b9a0b2db4a9a8d398dd29dd3ec33
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/0fe9b9a0b2db4a9a8d398dd29dd3ec33?variant=devin
Requested by: @HuiJun