Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Real numbers, based on Cauchy sequences #2487

Draft
wants to merge 19 commits into
base: master
Choose a base branch
from
Draft

Conversation

Taneb
Copy link
Member

@Taneb Taneb commented Sep 27, 2024

Very much work-in-progress

From previous attempts, multiplication of real numbers will be the stalling point. But the HoTT book suggests this can be implemented in terms of addition, subtraction, and multiplication by a rational, which are all achievable, and squaring, which I don't know about.

Copy link
Contributor

@MatthewDaggitt MatthewDaggitt left a comment

Choose a reason for hiding this comment

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

This looks great!

I've left a review on the two files outside of Data.Real. I think it's probably easier to try and merge them in first and then tackle the Real files?

d-indiscernable {p} {q} ∣p-q∣≡0 = begin
p ≡˘⟨ +-identityʳ p ⟩
p - 0ℚ ≡˘⟨ cong (_-_ p) (∣p∣≡0⇒p≡0 (p - q) ∣p-q∣≡0) ⟩
p - (p - q) ≡⟨ cong (_+_ p) (neg-distrib-+ p (- q)) ⟩
Copy link
Contributor

Choose a reason for hiding this comment

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

It feels like p - (p - q) ≡ q should be a lemma in GroupProperties?

Copy link
Member Author

Choose a reason for hiding this comment

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

It actually requires commutativity! (this is hidden in neg-distrib-+). As far as I can tell it doesn't exist yet.

Copy link
Contributor

Choose a reason for hiding this comment

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

The following proof looks commutativity-free to me?

p - (p - q) = p + - (p + - q) = p + (-p + q) = (p + -p) + q = 0# + q = q

Copy link
Member Author

Choose a reason for hiding this comment

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

The step - (p + - q) = - p + q requires commutativity

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh whoops. Yes, you don't get distributivity for free.

Copy link
Contributor

Choose a reason for hiding this comment

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

See Algebra.Properties.AbelianGroup.⁻¹-anti-homo‿- added in #2349 ?

src/Data/Rational/Properties.agda Outdated Show resolved Hide resolved
src/Data/Rational/Properties.agda Outdated Show resolved Hide resolved

private
d : ℚ → ℚ → ℚ
d p q = ∣ p - q ∣
Copy link
Contributor

Choose a reason for hiding this comment

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

This is really very frustrating. Anyone have any nice ideas how we can make this a function in Data.Rational.Base? I guess we could use some weird unicode bars?

Copy link
Contributor

Choose a reason for hiding this comment

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

No idea on how to name this properly in Data.Rational.Base. But I was surprised that it is ℚ-valued - do we not have non-negative rationals as a type? That way this would be nonNegative by construction instead of requiring a proof.

Copy link
Contributor

Choose a reason for hiding this comment

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

See my global comment: we should have ℚ⁺ as a first class thing in the library before we proceed!

Copy link
Contributor

@jamesmckinna jamesmckinna Oct 3, 2024

Choose a reason for hiding this comment

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

Re: notation. UPDATED since I learnt how to write the symbols ;-)
It is a norm, so does using the following offer any assistance?

Suggested change
d p q = ∣ p - q ∣
-- distance function
∥_─_∥ :
∥ p ─ q ∥ = ∣ p - q ∣

which reuses the 'long minus' \ - - - (used in Data.List.Relation.Unary.Any so hopefully the two uses won't be in scope at the same time...), and whose "weird Unicode bars" here are in fact simply given by \ | |... so they aren't too bad, either...

open import Relation.Binary
open import Relation.Binary.PropositionalEquality using (cong₂)

record CauchySequence : Set a where
Copy link
Contributor

Choose a reason for hiding this comment

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

So I think this and the equality definition needs to be defined over a DistanceFunction rather than a full Metric with laws. Otherwise we're forced to define completeness over the whole metric space bundle, and we try and avoid defining predicates over bundles like that in the library.

Copy link
Contributor

Choose a reason for hiding this comment

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

The added advantage of this, is that we could definite the notion of Cauchy sequence generically, rather than just for Rational.

Copy link
Contributor

Choose a reason for hiding this comment

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

This record would also benefit from a constructor I think? Which would allow us to avoid writing sequence x and sequence y everywhere and instead write x and y?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm. I want to make isCauchy irrelevant. Would both doing that and introducing a constructor run into Irrelevant Projections stuff?

Copy link
Contributor

Choose a reason for hiding this comment

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

Well we use a record constructor with irrelevant fields in Data.Rational.Base and I'm not aware we've run into any problems there...

Copy link
Contributor

Choose a reason for hiding this comment

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

See also Data.Refinement for an already-'officially-sanctioned' stdlib approach to such matters...

Copy link
Contributor

Choose a reason for hiding this comment

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

As for irrelevant projections, the discussion on agda/agda#7063 , upstream from #2199 (and others label:irrelevance/label:irrelevant-projections) suggest... (probably) not?

src/Function/Metric/Rational/CauchySequence.agda Outdated Show resolved Hide resolved
src/Function/Metric/Rational/CauchySequence.agda Outdated Show resolved Hide resolved
@MatthewDaggitt MatthewDaggitt added this to the v2.2 milestone Sep 28, 2024
Copy link
Contributor

@JacquesCarette JacquesCarette left a comment

Choose a reason for hiding this comment

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

This is looking really great. A variety of comments as I was leafing through the code.

ε : A

assoc : Associative _≈_ _∙_ → Associative (Pointwise _≈_) (Stream.zipWith _∙_)
head (assoc assoc₁ xs ys zs) = assoc₁ (head xs) (head ys) (head zs)
Copy link
Contributor

Choose a reason for hiding this comment

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

Here (and for all the newly added bits): assoc assoc₁ is super ugly. One of the two should be renamed.


private
d : ℚ → ℚ → ℚ
d p q = ∣ p - q ∣
Copy link
Contributor

Choose a reason for hiding this comment

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

No idea on how to name this properly in Data.Rational.Base. But I was surprised that it is ℚ-valued - do we not have non-negative rationals as a type? That way this would be nonNegative by construction instead of requiring a proof.

d-triangle p q r = begin
∣ p - r ∣ ≡⟨ cong (λ # → ∣ # - r ∣) (+-identityʳ p) ⟨
∣ p + 0ℚ - r ∣ ≡⟨ cong (λ # → ∣ p + # - r ∣) (+-inverseˡ q) ⟨
∣ p + (- q + q) - r ∣ ≡⟨ cong (λ # → ∣ # - r ∣) (+-assoc p (- q) q) ⟨
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this 'middle4' for semigroup? (i.e. all these +-assoc moves).

(lookup-zipWith m ℚ._+_ (sequence x) (sequence y))
(lookup-zipWith n ℚ._+_ (sequence x) (sequence y))
∣ (lookup (sequence x) m ℚ.+ lookup (sequence y) m) - (lookup (sequence x) n ℚ.+ lookup (sequence y) n) ∣
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like some notation for lookup (sequence x) m would make all of this way more readable.

open ≤-Reasoning
instance _ : Positive (½ * ε)
_ = pos*pos⇒pos ½ ε
[x] = isCauchy x (½ * ε)
Copy link
Contributor

Choose a reason for hiding this comment

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

you name isCauchy but only use its first component -- maybe name that instead, to save all the projections above?

Copy link
Member Author

Choose a reason for hiding this comment

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

I do use the second component, on lines 50 and 54. But I use the first component a lot more

src/Data/Real/Base.agda Outdated Show resolved Hide resolved
src/Data/Real/Base.agda Outdated Show resolved Hide resolved
lemma = solve 4 (λ a b c d → ((a :+ b) :- (c :+ d)) , ((a :- c) :+ (b :- d))) refl
where open +-*-Solver

+-assoc : Associative _+_
Copy link
Contributor

Choose a reason for hiding this comment

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

all the next properties seem to be lifting properties, i.e. if some binary operation + has property P then its lift to streams will have the same property.

@jamesmckinna
Copy link
Contributor

jamesmckinna commented Sep 29, 2024

I've written this on every channel concerned with constructive formalisations of the reals, but for the sake of clarity, I'll repeat myself here: please please please do NOT base the representation of on Cauchy sequences, because the rates of convergence (be it by 1/n convergence, or by 1/2^n...) are so bad, and so much pointless arithmetic is involved, but instead use the development in Russell O' Connor's PhD thesis (which we have had for 15+ years already...) in terms of the monad for metric space completion, based on functions from ε ∈ ℚ⁺ to representing nested decreasing ε-balls around a point... (IIRC; but it's all very clear in Russell's thesis, Coq or no Coq...)

The opportunity when passing from 'classical'/'orthodox' constructions in mathematics to ones 'better adapted' to the activity for formalisation, is one of the outstanding research challenges for our field, and a much-neglected one. We can (and should!!!) do better!

@Taneb
Copy link
Member Author

Taneb commented Sep 30, 2024

@jamesmckinna I'm reading Russel O'Connor's construction now. I haven't fully understood it yet, but I will note: I'm not using a fixed rate of convergence, so I don't think I have the rate of convergence issues you mention. For example, real number expressions involving only rational numbers so far converge immediately.

@jamesmckinna
Copy link
Contributor

jamesmckinna commented Oct 3, 2024

To press on my point: I think it's much more natural (Occam's Razor) to consider (regular) functions rather than streams for encoding the basic data underlying the 'Cauchy' property... and, given that stdlib does not commit to using HoTT-like quotients based on HI(I)Ts, in favour of a (legacy/historical, but still, ontologically less committed/more neutral) choice based on setoids, I don't see any especial need to mutually define distance-related concepts with the metric completion functor. But other opinions/design decisions are available!

@Taneb
Copy link
Member Author

Taneb commented Oct 8, 2024

This has stalled while I've been going on a sidequest to implement O'Connor-style reals. It's a lot more up-front work, but it does seem like it's going to win some advantages:

  • It avoids going via a stream of rationals, skipping the potentially long indexing step and avoiding needing --guardedness
  • A lot of the work implementing operators is done "up front", making the actual operations a lot easier

Both in this branch and my branch implementing the O'Connor reals (not currently online), I'm using Data.Rational.Solver a lot. This would be a little nicer perhaps if #1879 had been done, and if we were more willing to use these solvers within stdlib itself. It would also be very nice to have an inequality solver for rationals, but I have no idea how one could go about implementing that. Alternatively, I could put a bit of effort into factoring out common lemmas (mostly relating to ½ * x + ½ * x ≡ x) which might be enough to make this a lot less tedious.

Another thing that would be useful would be a type of strictly positive rationals, for example:

record ℚ⁺ : Set where
  constructor 𝕢⁺
  field
    unℚ⁺ :instance .{{unℚ⁺-pos}} : Positive unℚ⁺

(see also #1118). This looks like Data.Refinement, but it's not because of the instance-iness.

O'Connor defines metric spaces in terms of balls rather than distance functions, which allows him to define "real" metrics prior to having real numbers. Most of the challenge has been defining completion as a (strong!) monad on the category of these metric spaces and universally continuous functions.

@jamesmckinna
Copy link
Contributor

Very interesting! Thanks for the update regarding "O'Connor Reals" (he'd probably say they they were Bishop/Bridges/Richman...?), and while I'm sorry for holding things up, I'm not actually sorry ;-)

As for deciding inequalities between rationals, this is a classic in the Automated Reasoning literature, via Shostak's algorithm for combining decision procedures, but may be tackled directly via the decidability (via quantifier elimination) of (the theory of) Dense Linear Order... of which the Rationals are the unique up-to countable model. (I added the proofs for density a while back...)

Definitely agree that we should tackle solvers for this and other similar theories, but I haven't quite got my head round the Reflection machinery yet...

@jamesmckinna
Copy link
Contributor

jamesmckinna commented Oct 9, 2024

@Taneb writes:

Another thing that would be useful would be a type of strictly positive rationals, for example:

record ℚ⁺ : Set where
  constructor 𝕢⁺
  field
    unℚ⁺ :instance .{{unℚ⁺-pos}} : Positive unℚ⁺

(see also #1118). This looks like Data.Refinement, but it's not because of the instance-iness.

I'm confused... I though that the {{...}} bracketing would already mark the unℚ⁺-pos field as an instance, and available for proof search? What's the difference between your definition and eg.

record ℚ⁺ : Set where
  constructor 𝕢⁺
  field
    unℚ⁺ : ℚ
    .{{unℚ⁺-pos}} : Positive unℚ⁺

As for constructor/field names, my (ongoing; following PhIl Wadler) preference might be for 'minimum-ink' solutions, such as a postfix constructor _⁺ (or else as a pattern synonym taking explicit instance argument {{_}}?) and, whatever the actual field name chosen for the underlying value (though value springs to mind, echoing Data.Refinement... though I'd be inclined to keep it private?), a postfix synonym _⁻ to strip off the _⁺... but perhaps that might cause too much eye/mind strain? And pos would seem canonical for the associated irrelevant instance name?

@Taneb
Copy link
Member Author

Taneb commented Oct 9, 2024

Thanks @jamesmckinna , I've taken those suggestions into account. I'd been over-cautious with the instance declaration, and assumed I was right when Agda didn't complain about the redundant keyword

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants