You can express such theorems, but existing SMT solvers aren't strong enough to prove them out-of-the box, at least not for the time being. This is because SMT solvers do not do induction, and such claims require induction for their proofs.
To illustrate, this is how you'd write the theorem you want in z3's Python API:
from z3 import *
dummy = Int('dummy')
Sum = RecFunction("Sum", IntSort(), IntSort())
RecAddDefinition(Sum, [dummy], If(dummy == 0, 0, dummy + Sum(dummy-1)))
def P(n):
return Sum(n) == n * (n+1) / 2
n = Int('n')
prove(P(n))
Alas, if you run this you'll see that z3 loops forever.
In certain cases, you can get away by posing the inductive argument yourself:
from z3 import *
dummy = Int('dummy')
Sum = RecFunction("Sum", IntSort(), IntSort())
RecAddDefinition(Sum, [dummy], If(dummy == 0, 0, dummy + Sum(dummy-1)))
def P(n):
return Sum(n) == n * (n+1) / 2
s = Solver()
# Induction. Base case:
s.add(P(0))
# Inductive-step. Assert for `n`. Assert the negation for `n+1`, hoping for unsat answer.
n = Int('n')
s.add(P(n))
s.add(Not(P(n+1)))
print(s.check())
Alas, this loops forever as well. The tool is just not capable of handling the required step.
You can also try adding Sum
as an uninterpreted function, add axioms about it, and see if you can use some patterns to push the proof through. Alas, none of these attempts are very satisfactory. Even if you get a proof, it'll be brittle.
Bottom line, SMT solvers don't do induction, at least not for the time being. But the SMTLib spec allows for recursive function definitions, which naturally require inductive reasoning capabilities. Perhaps things will get better in the future, with possible user annotations, helping the solver find the inductive invariant. But, as it stands in early 2023, if you want to prove such theorems, you need to use actual theorem-provers, such as Isabelle, Coq, Lean, ACL2, etc.; or systems like Dafny, why3, etc., which automate inductive proofs much better than SMT solvers themselves.