I'm working with linear problems on rationals in Z3. To use Z3 I take SBV.
An example of a problem I pose is:
import Data.SBV
solution1 = do
x <- sRational "x"
w <- sRational "w"
constrain $ x.< w
constrain $ x + 2*w .>=0 .|| x .== 1
My question is:
Are these kinds of problems decidable?
I couldn't find a list of decidable theories or a way to tell if a theory is decidable.
The closest I found is this. The theory about the real ones is decidable, but is it the same for rational numbers? Intuition tells me that it is, but I have not found the information that allows me to assure it.
Thanks in advance
SBV models rationals using the standard "two integers" idea; that is, it represents the numerator and the denominator separately as integers. This means that if you add two symbolic rationals, you'll have a non-linear term over the integers. So, in theory, the problem will be in the semi-decidable fragment. That is, even if you restrict your multiplications to concrete scalars, addition of symbolic rationals will give rise to non-linear terms over integers.
Having said that, I had good luck using rationals; where z3 was able to decide most problems of interest without much difficulty. If it proves to be an issue, you should switch to SReal type (i.e., algebraic reals), for which z3 has a decision procedure. But of course, the models you get can now include algebraic reals, such as square-root-of-2, etc. (i.e., the roots of any polynomial with integer coefficients.)
Side note If your problem allows for delta-sat (i.e., satisfiability with perturbations), you should look into dReal (http://dreal.github.io), which SBV also supports as a backend solver. But perhaps that's not what you had in mind.
Theoretical note
Strictly speaking, linear arithmetic over rationals is decidable; see Section 3 of https://www.cs.ox.ac.uk/people/james.worrell/lecture15-2015.pdf for a proof. However, SMT solvers do not support rationals out-of-the-box; and SBV (as I mentioned above), uses two symbolic integers to represent rationals. So, adding two rationals will give rise to multiplication of two symbolic integers, taking you out of the decidable fragment. Of course, in practice, the solvers are quite adept at coming up with solutions even in the presence of non-linear terms; it's just that you're not always guaranteed. So, a more strict answer to your question is while linear arithmetic over rationals is decidable, the translation used by SBV puts the problem into the non-linear integer arithmetic domain, and hence decidability is not guaranteed. In any case, SMTLib does not come with a theory of rationals, so you're kind of out-of-luck when it comes to first class support for them.
I guess a rational solution will exist iff an integer solution exists to a suitably scaled collection of constraints. For example, x=1/2(=5/10), w=3/5(=6/10) is a solution to your example problem. Scaling your problem by 10, we have the equivalent constraint set:
10*x < 10*w
(10*x + 20*w >= 0) || (10*x == 10)
Writing x'=10*x and w'=10*w, this means that x'=5, w'=6 is an integer solution to:
x' < w'
(x' + w' >= 0) || (x' == 10)
Presburger famously showed that first-order logic plus integers and addition is decidable. (Multiplication by a constant is also allowed, since it can be expanded to an addition -- e.g. 3*x is x+x+x.)
I guess the only trick left is to show that it's possible to choose what scaling to use without having solved the problem yet. Nothing obvious occurs to me off the top of my head, but it seems reasonable that this should be doable. For example, perhaps if you take the product of all the nonzero numerators and denominators in your constraint set, you can show that the set of rationals with that product as their denominator is indistinguishable from the full set of rationals. (If so, you could look through the proof to see if it still works with a smaller denominator.)
I'm not a z3 expert, so I can't talk about how this translates to whether that tool specifically is suitable, but it seems likely to me that it is possible to create a suitable tool.
Related
Disclaimer: I'm a total Isabelle beginner.
I'm trying to export the "sqrt" function or rather functions and definitions using "sqrt" to Haskell. My first try was just:
theory Scratch
imports Complex_Main
begin
definition val :: "real" where "val = sqrt 4"
export_code val in Haskell
end
Which resulted in the following error:
Wellsortedness error
(in code equation root ?n ?x ≡
if equal_nat_inst.equal_nat ?n zero_nat_inst.zero_nat then zero_real_inst.zero_real
else the_inv_into top_set_inst.top_set
(λy. times_real_inst.times_real (sgn_real_inst.sgn_real y)
(abs_real_inst.abs_real y ^ ?n))
?x,
with dependency "val" -> "sqrt" -> "root"):
Type real not of sort {enum,equal}
No type arity real :: enum
So I tried to replace "sqrt" with Haskell's "Prelude.sqrt":
code_printing
constant sqrt ⇀ (Haskell) "Prelude.sqrt _"
export_code val in Haskell
Which still resulted in the same error. Which seems rather odd to me, because replacing "plus" with some arbitrary function "f" seems to be fine:
definition val' :: "nat" where "val' = plus 49 1"
code_printing
constant plus ⇀ (Haskell) "_ `f` _"
export_code val' in Haskell
How do I resolve this issue?
I'm not sure about the code_printing issue, but what do you expect to happen here? Wellsortedness error during code generation usually means that what you're trying to export is simply not computable (or at least Isabelle doesn't know how).
What do you expect something like sqrt 2 to compile to in Haskell? What about sqrt pi? You cannot hope to generate executable code for all real numbers. Isabelle's default implementation restricts itself to rational numbers.
Doing code-printing to replace Isabelle's sqrt with Haskell's sqrt is only going to give you a type error, since Haskell's sqrt works on floating point numbers, and not on Isabelle's exported real type.
There is a file in ~~/src/HOL/Library/Code_Real_Approx_By_Float that maps Isabelle's operations on real numbers to floating point approximations in Standard ML and OCaml, but this is for experimentation only, since you lose all correctness guarantees if you do that sort of thing.
Lastly, there is an entry in the Archive of Formal Proofs that provides exact executable algebraic real numbers, so that you can do at least some operations with square root etc., but this is a big piece of work and the performance can be pretty bad in some cases.
There is also a sqrt operation on natural numbers in Isabelle (i.e. it rounds down) in ~~/src/HOL/Library/Discrete, and that can easily be exported to Haskell.
In the AFP there also is an entry Sqrt_Babylonian, which contains algorithms to compute sqrt up to a given precision epsilon > 0, without any floating point rounding errors.
Regarding the complexity of algebraic numbers that Manuel mentioned, it really depends on your input. If you use nested square-roots or combine different square-roots (like sqrt 2 + ... + sqrt 50), then the performance will degrade soonish. However, if you rarely use square-roots or always use the same square-root in multiple locations, then algebraic numbers might be fast enough.
In Haskell, why is the infix alias of mappend (from class Monoid) <> instead of +? In algebra courses + is usually used for the binary operator of a monoid.
The function + is specific to numbers, and moreover, it's only one way to implement Monoid for numbers (* is equally valid). Similarly, with booleans, it would be equally valid to use && and ||. Using the symbol + suggests that Monoids are about addition specifically, when really they're just about any associative operation.
It is true that, at least in my experience, one is likely to use mappend in a fashion resembling addition: concatenating lists or vectors, taking unions of sets or maps, etc, etc. However, the Haskell mindset favors generality and adherence to mathematical principles over (arguably) what is more intuitive. It's certainly reasonable, in my opinion, to think of mappend as a sort of general addition, and make adjustments in the cases where it isn't.
Partly due to the principle of least astonishment and partly because there are at least two sensible monoid instances for numbers (namely, Sum and Product from Data.Monoid).
It seems to me that the Num type class consists of a pretty arbitrary collection of functions. There are lots of types that naturally have + and * operations, but are problematic as instances of Num, due to the presence of abs, signum, and fromInteger. I can't find any discussion of the design philosophy behind this class, so it's not clear to me if there is a sensible rationale here, or if it's an unfortunate historical oddity.
I'll give an illustration of my question. Suppose I'm implementing a Matrix class, with components that are Doubles. I can obviously implement +, *, -, and negate. Maybe fromInteger x could give a 1x1 Matrix with component the Double value fromInteger x. It's less obvious what to do with abs and signum, but I could come up with something that satisfies the rule (from the class's documentation):
abs x * signum x == x
The objection to this idea is that my instance of Num is not fulfilling some implicit rules that people expect of Num. My * is a partial function (assuming the size of the Matrix is a runtime parameter), which is not true for the usual instances like Double and Int. And it doesn't commute. Whatever I come up with for abs and signum are not going to satisfy everyone's expectations.
The objection to this objection is that my Matrix multiplication is going to be a partial function anyway (and in this kind of type that seems to be accepted in the Haskell community), so why does it matter if it's * in particular that is the partial function? And if my abs and signum satisfy the rule from the documentation, then I've fulfilled my side of the bargain. Anyone relying on anything more from a Num instance is in the wrong.
Should a type like Matrix be an instance of Num?
Don't make Num instances for non-rings. It's just confusing.
Of course, you can often define instances which do something useful, but if it's not completely obvious what then better just define a plain function with descriptive name, or some weaker class instance with better-defined semantics. If somebody wants to use this with short operators or polymorphic Num functions, they can still define that locally in their own module (preferrably with a simple newtype wrapper.
In particular, a Num-instance for general (dynamically-sized) matrices is problematic because it's not obvious what should happen when the dimensions don't match. What behaviour do you want to generalise?
What I would consider a good instance is matrices of fixed quadratic size (i.e. linear endomorphisms on a given vector space). In this case, multiplication is evidently composition†, and number literals would be taken as constant-diagonal matrices, so 1 is actually multiplicative identity. Like what you write in maths contexts.
But that's not compatible with your idea of arbitrarily choosing the size of number-literals as 1×1! People would expect 2 * m to work, but it crashes. Well, better crash than give unexpected results; unfortunately it's tempting to come up with some clever way of defining multiplication in a suitable way. For instance, we could block-diagonal-copy the smaller matrix until it's large enough, perhaps only do this in the 1×1 case... well, Matlab does this kind of ad-hoc stuff, but please! let's not take such a horrible language as a model for what's good ideas.
If you have something that's obviously an additive group, indeed vector space, then make it a VectorSpace! If you also have a multiplication, but it's partial, then better only define it as a plain function.
If you fancy, you can define instances for the fine finely staggered numeric-prelude classes. Personally (though I like the idea of this project) I couldn't yet be bothered to use it anywhere, because it's rather an efford to understand the hierarchy.
†Or is it? Trouble already starts here, I think hmatrix actually implements * on matrices as element-wise multiplication. That's more horrible than Matlab!
In Eiffel one is allowed to use an expanded class which doesn't allocate from the heap. From a developer's perspective one rarely has to think about conversion from Int to Float as it is automatic. My question is this: Why did Haskell not choose a similar approach to modeling Num. Specifically, lets consider the Int instance. Here is the rationale for my question:
[1..3] = [1,2,3]
[1..3.5] = [1.0,2.0,3.0,4.0] -- rounds up
The second list was something that I was not expecting because there are by definition infinite floating point numbers between any two integers. Of course once we test the sequence it is clear that it is returning the floor of the floating point number rounded up. One of the reasons these conversions are needed is allow us to compute mean of a set of Integers for example.
In Eiffel the number type hierarchy is a bit more programmer friendly and the conversion happens as needed: for example creating a sequence can still be a set of Ints that result in a floating point mean. This has a readability advantage.
Is there a reason that expanded class was not implemented in Haskell?Any references will greatly help.
#ony: the point about parallel strategies: wont we face the same issue when using primitives? The manual does discourage using primitives and that makes sense to me in general where ever we can use primitives we probably need to use the abstract type. The issue I faced when trying to a mean of numbers is the missing Fractional Int instance and as to why does 5/3 not promote to a floating point instead of having to create floating point array to achieve the same result. There must be a reason as to why Fractional instance of Int and Integer is not defined? That could help me understand the rationale better.
#leftroundabout: the question is not about expanded classes per se but the convenience that such a feature can offer although that feature alone is not sufficient to handle the type promotion to float from an int for example as mentioned in my response to #ony. Lets take the classic example of a mean and try to define it as
> [Int] :: Double
let mean xs = sum xs / length xs (--not valid haskell code)
[Int] :: Double
let mean = sum xs / fromIntegral (length xs)
I would have liked it if I did not have to call the fromIntegral to get the mean function to work and that ties to the missing Fractional Int. Although the explanation seems to make sense, it has to, what I dont understand is if I am clear that I expect a double and I state it in my type signature is that not sufficient to do the appropriate conversion?
[a..b] is shorthand for enumFromTo a b, a method of the Enum typeclass. It begins at a and succs until the first time b is exceeded.
[a,b..c] is shorthand for enumFromThenTo a b c is similar to enumFromTo except instead of succing it adds the difference b-a each time. By default this difference is computed by roundtripping through Int so fractional differences may or may not be respected. That said, Double works as you'd expect
Prelude> [0.0, 0.5.. 10]
[0.0,0.5,1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5,7.0,7.5,8.0,8.5,9.0,9.5,10.0]
[a..] is shorthand for enumFrom a which just succs forever.
[a,b..] is shorthand for enumFromThen a b which just adds (b-a) forever.
As for behaviour #J.Abrahamson already replied. That's definition enumFromThenTo.
As for design...
Actually GHC have Float# that represents unboxed type (can be allocated anywhere, but value is strict).
Since Haskell is a lazy language it assumes that most of the values are not required initially, until they actually referred with a primitive with a strict arguments.
Consider length [2..10]. In this case without optimization Haskell may even avoid generation of numbers and simply build up a list (without values). Probably more useful example takeWhile (<100) [x*(x-1) | x <- [2..]].
But you shouldn't think that we have overhead here since you are writing in language that abstracts away all that stuff with thumbs (except of strict notation). Haskell compiler have to take this as a work for itself. I.e. when compiler will be able to tell that all elements of list will be referenced (transformed to normal form) and it decides to process it within one stack of returns it can allocate it on stack.
Also with such approach you can gain more out of your code by using multiple CPU cores. Imagine that using Strategies your list being processed on a different cores and thus they should share common data on heap (not on stack).
In System F I can define the genuine total addition function using Church numerals.
In Haskell I cannot define that function because of the bottom value. For example, in haskell if x + y = x, then I cannot say that y is zero - if x is bottom, x + y = x for any y. So the addition is not the true addition but an approximation to it.
In C I cannot define that function because C specification requires everything to have finite size. So in C possible approximations are even worse than in Haskell.
So we have:
In System F it's possible to define the addition but it's not possible to have a complete implementation (because there are no infinite hardware).
In Haskell it's not possible to define the addition (because of the bottom), and it's not possible to have a complete implementation.
In C it's not possible to define the total addition function (because semantic of everything is bounded) but compliant implementations are possible.
So all 3 formal systems (Haskell, System F and C) seem to have different design tradeoffs.
So what are consequences of choosing one over another?
Haskell
This is a strange problem because you're working with a vague notion of =. _|_ = _|_ only "holds" (and even then you should really use ⊑) at the domain semantic level. If we distinguish between information available at the domain semantic level and equality in the language itself, then it's perfectly correct to say that True ⊑ x + y == x --> True ⊑ y == 0.
It's not addition that's the problem, and it's not natural numbers that are the problem either -- the issue is simply distinguishing between equality in the language and statements about equality or information in the semantics of the language. Absent the issue of bottoms, we can typically reason about Haskell using naive equational logic. With bottoms, we can still use equational reasoning -- we just have to be more sophisticated with our equations.
A fuller and clearer exposition of the relationship between total languages and the partial languages defined by lifting them is given in the excellent paper "Fast and Loose Reasoning is Morally Correct".
C
You claim that the C requires everything (including addressable space) to have a finite size, and therefore that C semantics "impose a limit" on the size of representable naturals. Not really. The C99 standard says the following: "Any pointer type may be converted to an integer type. Except as previously specified, the
result is implementation-defined. If the result cannot be represented in the integer type,
the behavior is undefined. The result need not be in the range of values of any integer
type." The rationale document further emphasizes that "C has now been implemented on a wide range of architectures. While some of these
architectures feature uniform pointers which are the size of some integer type, maximally
portable code cannot assume any necessary correspondence between different pointer types and the integer types. On some implementations, pointers can even be wider than any integer type."
As you can see, there's explicitly no assumption that pointers must be of a finite size.
You have a set of theories as frameworks to do your reasoning with; finite reality, Haskell semantics, System F are just ones of them.
You can choose appropriate theory for your work, build new theory from scratch or from big pieces of existing theories gathered together. For example, you can consider set of always terminating Haskell programs and employ bottomless semantics safely. In this case your addition will be correct.
For low level language there may be considerations to plug finiteness in but for high level language it is worth to omit such things because more abstract theories allow wider application.
While programming, you use not "language specification" theory but "language specification + implementation limitations" theory so there is no difference between cases where memory limits present in language specification or in language implementation. Absence of limits become important when you start building pure theoretic constructions in framework of language semantics. For example, you may want to prove some program equivalences or language translations and find that every unneeded detail in language specification brings a much pain in proof.
I'm sure you've heard the aphorism that "in theory there is no difference between theory and practice, but in practice there is."
In this case, in theory there are differences, but all of these systems deal with the same finite amount of addressable memory so in practice there is no difference.
EDIT:
Assuming you can represent a natural number in any of these systems, you can represent addition in any of them. If the constraints you are concerned about prevent you from representing a natural number then you can't represent Nat*Nat addition.
Represent a natural number as a pair of (heuristic lower bound on the maximum bit size and a lazily evaluated list of bits).
In the lambda calculus, you can represent the list as a function that returns a function that called with true returns the 1's bit, and called with false returns a function that does the same for the 2's bit and so on.
Addition is then an operation applied to the zip of those two lazy lists that propagates a carry bit.
You of course have to represent the maximum bit size heuristic as a natural number, but if you only instantiate numbers with a bit count that is strictly smaller than the number you are representing, and your operators don't break that heuristic, then the bit size is inductively a smaller problem than the numbers you want to manipulate, so operations terminate.
On the ease of accounting for edge cases, C will give you very little help. You can return special values to represent overflow/underflow, and even try to make them infectious (like IEEE-754 NaN) but you won't get complaints at compile time if you fail to check. You could try and overload a signal SIGFPE or something similar to trap problems.
I cannot say that y is zero - if x is bottom, x + y = x for any y.
If you're looking to do symbolic manipulation, Matlab and Mathematica are implemented in C and C like languages. That said, python has a well-optimized bigint implementation that is used for all integer types. It's probably not suitable for representing really really large numbers though.