gargamelle-fol (empty) → 0.1.0.0
raw patch · 9 files changed
+1043/−0 lines, 9 filesdep +basedep +containersdep +gargamelle-core
Dependencies added: base, containers, gargamelle-core, gargamelle-fol, gargamelle-model, hspec, polytropos-core, process
Files
- CHANGELOG.md +7/−0
- LICENSE +189/−0
- README.md +171/−0
- gargamelle-fol.cabal +62/−0
- src/FirstOrder.hs +183/−0
- src/Gargamelle/FOL/Z3.hs +154/−0
- src/Hyperdoctrine.hs +111/−0
- src/RefinementCalculus.hs +54/−0
- test/Main.hs +112/−0
+ CHANGELOG.md view
@@ -0,0 +1,7 @@+# Changelog for gargamelle-fol++## 0.1.0.0 — Initial Release++- First-order logic institution and streaming Z3 SMT solver pipeline.+- Hyperdoctrine structures and quantifier elimination.+- Refinement calculus generation for formal program verification.
+ LICENSE view
@@ -0,0 +1,189 @@+EUROPEAN UNION PUBLIC LICENCE v. 1.2+EUPL © the European Union 2007, 2016++This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined below) which is provided under the+terms of this Licence. Any use of the Work, other than as authorised under this Licence is prohibited (to the extent such+use is covered by a right of the copyright holder of the Work).+The Work is provided under the terms of this Licence when the Licensor (as defined below) has placed the following+notice immediately following the copyright notice for the Work:+ Licensed under the EUPL+or has expressed by any other means his willingness to license under the EUPL.++1.Definitions+In this Licence, the following terms have the following meaning:+— ‘The Licence’:this Licence.+— ‘The Original Work’:the work or software distributed or communicated by the Licensor under this Licence, available+as Source Code and also as Executable Code as the case may be.+— ‘Derivative Works’:the works or software that could be created by the Licensee, based upon the Original Work or+modifications thereof. This Licence does not define the extent of modification or dependence on the Original Work+required in order to classify a work as a Derivative Work; this extent is determined by copyright law applicable in+the country mentioned in Article 15.+— ‘The Work’:the Original Work or its Derivative Works.+— ‘The Source Code’:the human-readable form of the Work which is the most convenient for people to study and+modify.+— ‘The Executable Code’:any code which has generally been compiled and which is meant to be interpreted by+a computer as a program.+— ‘The Licensor’:the natural or legal person that distributes or communicates the Work under the Licence.+— ‘Contributor(s)’:any natural or legal person who modifies the Work under the Licence, or otherwise contributes to+the creation of a Derivative Work.+— ‘The Licensee’ or ‘You’:any natural or legal person who makes any usage of the Work under the terms of the+Licence.+— ‘Distribution’ or ‘Communication’:any act of selling, giving, lending, renting, distributing, communicating,+transmitting, or otherwise making available, online or offline, copies of the Work or providing access to its essential+functionalities at the disposal of any other natural or legal person.++2.Scope of the rights granted by the Licence+The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, sublicensable licence to do the following, for+the duration of copyright vested in the Original Work:+— use the Work in any circumstance and for all usage,+— reproduce the Work,+— modify the Work, and make Derivative Works based upon the Work,+— communicate to the public, including the right to make available or display the Work or copies thereof to the public+and perform publicly, as the case may be, the Work,+— distribute the Work or copies thereof,+— lend and rent the Work or copies thereof,+— sublicense rights in the Work or copies thereof.+Those rights can be exercised on any media, supports and formats, whether now known or later invented, as far as the+applicable law permits so.+In the countries where moral rights apply, the Licensor waives his right to exercise his moral right to the extent allowed+by law in order to make effective the licence of the economic rights here above listed.+The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to any patents held by the Licensor, to the+extent necessary to make use of the rights granted on the Work under this Licence.++3.Communication of the Source Code+The Licensor may provide the Work either in its Source Code form, or as Executable Code. If the Work is provided as+Executable Code, the Licensor provides in addition a machine-readable copy of the Source Code of the Work along with+each copy of the Work that the Licensor distributes or indicates, in a notice following the copyright notice attached to+the Work, a repository where the Source Code is easily and freely accessible for as long as the Licensor continues to+distribute or communicate the Work.++4.Limitations on copyright+Nothing in this Licence is intended to deprive the Licensee of the benefits from any exception or limitation to the+exclusive rights of the rights owners in the Work, of the exhaustion of those rights or of other applicable limitations+thereto.++5.Obligations of the Licensee+The grant of the rights mentioned above is subject to some restrictions and obligations imposed on the Licensee. Those+obligations are the following:++Attribution right: The Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to+the Licence and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the+Licence with every copy of the Work he/she distributes or communicates. The Licensee must cause any Derivative Work+to carry prominent notices stating that the Work has been modified and the date of modification.++Copyleft clause: If the Licensee distributes or communicates copies of the Original Works or Derivative Works, this+Distribution or Communication will be done under the terms of this Licence or of a later version of this Licence unless+the Original Work is expressly distributed only under this version of the Licence — for example by communicating+‘EUPL v. 1.2 only’. The Licensee (becoming Licensor) cannot offer or impose any additional terms or conditions on the+Work or Derivative Work that alter or restrict the terms of the Licence.++Compatibility clause: If the Licensee Distributes or Communicates Derivative Works or copies thereof based upon both+the Work and another work licensed under a Compatible Licence, this Distribution or Communication can be done+under the terms of this Compatible Licence. For the sake of this clause, ‘Compatible Licence’ refers to the licences listed+in the appendix attached to this Licence. Should the Licensee's obligations under the Compatible Licence conflict with+his/her obligations under this Licence, the obligations of the Compatible Licence shall prevail.++Provision of Source Code: When distributing or communicating copies of the Work, the Licensee will provide+a machine-readable copy of the Source Code or indicate a repository where this Source will be easily and freely available+for as long as the Licensee continues to distribute or communicate the Work.+Legal Protection: This Licence does not grant permission to use the trade names, trademarks, service marks, or names+of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and+reproducing the content of the copyright notice.++6.Chain of Authorship+The original Licensor warrants that the copyright in the Original Work granted hereunder is owned by him/her or+licensed to him/her and that he/she has the power and authority to grant the Licence.+Each Contributor warrants that the copyright in the modifications he/she brings to the Work are owned by him/her or+licensed to him/her and that he/she has the power and authority to grant the Licence.+Each time You accept the Licence, the original Licensor and subsequent Contributors grant You a licence to their contributions+to the Work, under the terms of this Licence.++7.Disclaimer of Warranty+The Work is a work in progress, which is continuously improved by numerous Contributors. It is not a finished work+and may therefore contain defects or ‘bugs’ inherent to this type of development.+For the above reason, the Work is provided under the Licence on an ‘as is’ basis and without warranties of any kind+concerning the Work, including without limitation merchantability, fitness for a particular purpose, absence of defects or+errors, accuracy, non-infringement of intellectual property rights other than copyright as stated in Article 6 of this+Licence.+This disclaimer of warranty is an essential part of the Licence and a condition for the grant of any rights to the Work.++8.Disclaimer of Liability+Except in the cases of wilful misconduct or damages directly caused to natural persons, the Licensor will in no event be+liable for any direct or indirect, material or moral, damages of any kind, arising out of the Licence or of the use of the+Work, including without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, loss+of data or any commercial damage, even if the Licensor has been advised of the possibility of such damage. However,+the Licensor will be liable under statutory product liability laws as far such laws apply to the Work.++9.Additional agreements+While distributing the Work, You may choose to conclude an additional agreement, defining obligations or services+consistent with this Licence. However, if accepting obligations, You may act only on your own behalf and on your sole+responsibility, not on behalf of the original Licensor or any other Contributor, and only if You agree to indemnify,+defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against such Contributor by+the fact You have accepted any warranty or additional liability.++10.Acceptance of the Licence+The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ placed under the bottom of a window+displaying the text of this Licence or by affirming consent in any other similar way, in accordance with the rules of+applicable law. Clicking on that icon indicates your clear and irrevocable acceptance of this Licence and all of its terms+and conditions.+Similarly, you irrevocably accept this Licence and all of its terms and conditions by exercising any rights granted to You+by Article 2 of this Licence, such as the use of the Work, the creation by You of a Derivative Work or the Distribution+or Communication by You of the Work or copies thereof.++11.Information to the public+In case of any Distribution or Communication of the Work by means of electronic communication by You (for example,+by offering to download the Work from a remote location) the distribution channel or media (for example, a website)+must at least provide to the public the information requested by the applicable law regarding the Licensor, the Licence+and the way it may be accessible, concluded, stored and reproduced by the Licensee.++12.Termination of the Licence+The Licence and the rights granted hereunder will terminate automatically upon any breach by the Licensee of the terms+of the Licence.+Such a termination will not terminate the licences of any person who has received the Work from the Licensee under+the Licence, provided such persons remain in full compliance with the Licence.++13.Miscellaneous+Without prejudice of Article 9 above, the Licence represents the complete agreement between the Parties as to the+Work.+If any provision of the Licence is invalid or unenforceable under applicable law, this will not affect the validity or+enforceability of the Licence as a whole. Such provision will be construed or reformed so as necessary to make it valid+and enforceable.+The European Commission may publish other linguistic versions or new versions of this Licence or updated versions of+the Appendix, so far this is required and reasonable, without reducing the scope of the rights granted by the Licence.+New versions of the Licence will be published with a unique version number.+All linguistic versions of this Licence, approved by the European Commission, have identical value. Parties can take+advantage of the linguistic version of their choice.++14.Jurisdiction+Without prejudice to specific agreement between parties,+— any litigation resulting from the interpretation of this License, arising between the European Union institutions,+bodies, offices or agencies, as a Licensor, and any Licensee, will be subject to the jurisdiction of the Court of Justice+of the European Union, as laid down in article 272 of the Treaty on the Functioning of the European Union,+— any litigation arising between other parties and resulting from the interpretation of this License, will be subject to+the exclusive jurisdiction of the competent court where the Licensor resides or conducts its primary business.++15.Applicable Law+Without prejudice to specific agreement between parties,+— this Licence shall be governed by the law of the European Union Member State where the Licensor has his seat,+resides or has his registered office,+— this licence shall be governed by Belgian law if the Licensor has no seat, residence or registered office inside+a European Union Member State.++Appendix++‘Compatible Licences’ according to Article 5 EUPL are:+— GNU General Public License (GPL) v. 2, v. 3+— GNU Affero General Public License (AGPL) v. 3+— Open Software License (OSL) v. 2.1, v. 3.0+— Eclipse Public License (EPL) v. 1.0+— CeCILL v. 2.0, v. 2.1+— Mozilla Public Licence (MPL) v. 2+— GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3+— Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for works other than software+— European Union Public Licence (EUPL) v. 1.1, v. 1.2+— Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong Reciprocity (LiLiQ-R+).++The European Commission may update this Appendix to later versions of the above licences without producing+a new version of the EUPL, as long as they provide the rights granted in Article 2 of this Licence and protect the+covered Source Code from exclusive appropriation.+All other changes or additions to this Appendix require the production of a new EUPL version.
+ README.md view
@@ -0,0 +1,171 @@+# `gargamelle-fol` — First-Order Logic Institution, Hyperdoctrines & Streaming Z3 SMT-LIB2++[](https://haskell.org)+[](https://spdx.org/licenses/EUPL-1.2.html)+[](../../README.md#architectural-tier-map)++---++## 1. Executive Summary & Problem Framing++Predicate logic with quantifiers and relational constraints is fundamental to system verification, contract refinement, and theorem proving. However, embedding first-order logic (FOL) into modern multi-agent synthesis loops introduces major architectural hurdles:+1. **FFI Brittleness & ABI Drift:** Direct C/C++ FFI bindings to solvers like Z3 cause crashes across varying GHC versions (9.6–9.14) and complicate packaging across Nix, Stack, and Cabal.+2. **Opaque Solver Outputs:** Traditional solver interfaces return bare booleans (`Sat`/`Unsat`), discarding the countermodel witnesses necessary for automated fault repair and inductive learning.+3. **Context Weakening & Quantifier Inconsistency:** Ad-hoc substitution engines frequently introduce variable capture and fail to preserve the adjunction laws governing universal ($\forall$) and existential ($\exists$) quantifiers.++`gargamelle-fol` resolves these issues via rigorous categorical foundations and POSIX streaming:+- **First-Order Institution:** Embeds FOL into the Goguen-Burstall framework, supporting Herbrand universe evaluation and signature morphisms.+- **Lawvere Hyperdoctrines:** Formalizes quantifiers functorially as adjoints ($\exists_t \dashv t^* \dashv \forall_t$) to substitution functors over context categories.+- **Streaming SMT-LIB2 Pipe:** Emits standard SMT-LIB2 scripts streamed over POSIX pipes directly to host Z3 without FFI overhead.+- **Structured Evidence Extraction:** Automatically transforms Z3 countermodels into inspectable `Counterexample` witnesses and verified proofs into `VerifiedSection`.++---++## 2. Mathematical Foundations & Theoretical Invariants++### 2.1 Categorical Hyperdoctrine Quantifier Adjunctions+In Lawvere's categorical logic, a First-Order Hyperdoctrine is a functor $\mathcal{P} : \mathbf{C}^{\mathrm{op}} \to \mathbf{HeytAlg}$ indexing propositional fibers over a cartesian category of contexts $\mathbf{C}$:+```+ P(X × Y) (Fiber over extended context)+ ▲ ▲+ Substitution │ │ Universal Quantifier+ π* (Pullback)│ │ ∀_π (Right Adjoint)+ │ ▼+ P(X) (Fiber over base context)+```++1. **Pullback (Substitution):** For any projection or term substitution $\pi : X \times Y \to X$, the contravariant functorial action $\pi^* : \mathcal{P}(X) \to \mathcal{P}(X \times Y)$ models context weakening.+2. **Existential Quantifier as Left Adjoint:**+ $$\exists_\pi \dashv \pi^* \iff (\exists_\pi(\varphi) \le \psi \iff \varphi \le \pi^*(\psi))$$+3. **Universal Quantifier as Right Adjoint:**+ $$\pi^* \dashv \forall_\pi \iff (\pi^*(\psi) \le \varphi \iff \psi \le \forall_\pi(\varphi))$$+4. **Beck-Chevalley Invariance:** For any pullback square in $\mathbf{C}$, the canonical comparison between quantification and substitution commutes.++<!-- See Note [Categorical Hyperdoctrine Quantifier Adjunctions] [ref:note-hyperdoctrine-adjunctions] -->++### 2.2 Streaming SMT-LIB2 Piping & Dual Refutation+Interfacing with external solvers avoids foreign function bindings by utilizing streaming POSIX pipes:++```+┌─────────────────────┐ formulaToSMTLib ┌───────────────────────┐+│ FirstOrder Formula │ ───────────────────────────► │ SMT-LIB2 Script │+└─────────────────────┘ └───────────┬───────────┘+ │+ readProcessWithExitCode+ ▼+┌─────────────────────┐ parseZ3Model ┌───────────────────────┐+│ Evidence Witness │ ◄─────────────────────────── │ Host Z3 stdout/stderr │+│ (Counterexample / │ │ (sat / unsat) │+│ VerifiedSection) │ └───────────────────────┘+└─────────────────────┘+```++- **Dual Refutation:** To verify that formula $\varphi$ is valid, `verifyValidityZ3` tests whether $\neg \varphi$ is unsatisfiable.+- **Evidence Soundness:**+ - $\neg \varphi$ is $\mathrm{UNSAT} \implies \varphi$ holds universally, producing `VerifiedSection`.+ - $\neg \varphi$ is $\mathrm{SAT} \implies$ Z3 produces a model $\mathcal{M} \models \neg \varphi$. Parsing this model yields a concrete `Counterexample` containing the exact falsifying variable assignments.++<!-- See Note [Streaming Z3 SMT-LIB2 Piping and Model Extraction] [ref:note-z3-smt-pipe-extraction] -->++---++## 3. Module Architecture & Repertoire Breadth++The package exports 4 modules providing complete first-order verification and solver piping:++| Module | Primary Types & Typeclasses | Architectural Purpose |+| :--- | :--- | :--- |+| [`Gargamelle.FOL.Z3`](src/Gargamelle/FOL/Z3.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/Gargamelle-FOL-Z3.html)) | [`solveZ3Raw`](src/Gargamelle/FOL/Z3.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/Gargamelle-FOL-Z3.html#v:solveZ3Raw)), [`checkFormulaZ3`](src/Gargamelle/FOL/Z3.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/Gargamelle-FOL-Z3.html#v:checkFormulaZ3)), [`verifyValidityZ3`](src/Gargamelle/FOL/Z3.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/Gargamelle-FOL-Z3.html#v:verifyValidityZ3)), [`SMTResult`](src/Gargamelle/FOL/Z3.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/Gargamelle-FOL-Z3.html#t:SMTResult)) | Streaming SMT-LIB2 serialization, host Z3 process piping, and model parsing. |+| [`FirstOrder`](src/FirstOrder.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/FirstOrder.html)) | [`Formula`](src/FirstOrder.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/FirstOrder.html#t:Formula)), [`Term`](src/FirstOrder.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/FirstOrder.html#t:Term)), [`FOLModel`](src/FirstOrder.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/FirstOrder.html#t:FOLModel)), [`evalFormula`](src/FirstOrder.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/FirstOrder.html#v:evalFormula)) | Core first-order logic syntax, Herbrand model structures, and evaluation. |+| [`Hyperdoctrine`](src/Hyperdoctrine.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/Hyperdoctrine.html)) | [`Context`](src/Hyperdoctrine.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/Hyperdoctrine.html#t:Context)), [`HDCSig`](src/Hyperdoctrine.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/Hyperdoctrine.html#t:HDCSig)), [`HDModel`](src/Hyperdoctrine.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/Hyperdoctrine.html#t:HDModel)) | Lawvere hyperdoctrine fibered contexts and categorical institution integration. |+| [`RefinementCalculus`](src/RefinementCalculus.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/RefinementCalculus.html)) | [`Spec`](src/RefinementCalculus.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/RefinementCalculus.html#t:Spec)), [`spec`](src/RefinementCalculus.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/RefinementCalculus.html#v:spec)), [`refine`](src/RefinementCalculus.hs) ([Hackage](https://hackage.haskell.org/package/gargamelle-fol/docs/RefinementCalculus.html#v:refine)) | Morgan-Back-von Wright refinement calculus specifications and contract preorder. |++---++## 4. Executable Worked Examples++### 4.1 Automated Validity Verification via Z3 Pipe+```haskell+{-# LANGUAGE DataKinds #-}++module ExampleZ3Validity where++import Data.Proxy (Proxy(..))+import FirstOrder (Formula(..))+import Gargamelle.FOL.Z3 (verifyValidityZ3)+import Gargamelle.Model.Satisfaction (Evidence(..))++-- Verify the Law of Excluded Middle: (P \/ not P)+verifyLEM :: IO ()+verifyLEM = do+ let p = Pred (Proxy @"P") []+ tautology = Or p (Not p)+ evidence <- verifyValidityZ3 tautology+ case evidence of+ VerifiedSection -> putStrLn "Formally verified: tautology holds across all models."+ Counterexample sym _witness -> putStrLn $ "Disproved: countermodel found in " ++ sym+```++### 4.2 Satisfiability Model Extraction+```haskell+{-# LANGUAGE DataKinds #-}++module ExampleZ3Model where++import Data.Proxy (Proxy(..))+import FirstOrder (Formula(..))+import Gargamelle.FOL.Z3 (checkFormulaZ3, SMTResult(..))++-- Find satisfying model for (P /\ not Q)+findWitness :: IO ()+findWitness = do+ let p = Pred (Proxy @"P") []+ q = Pred (Proxy @"Q") []+ formula = And p (Not q)+ result <- checkFormulaZ3 formula+ case result of+ Sat model -> putStrLn $ "Satisfying model: " ++ show model+ Unsat -> putStrLn "Formula is unsatisfiable."+ Unknown e -> putStrLn $ "Solver inconclusive: " ++ e+```++---++## 5. Integration Points & Subsystem Boundaries++`gargamelle-fol` serves at **Tier 3 (Logic Engine)** of the architecture:++```+┌────────────────────────────────────────────────────────┐+│ Downstream Consumers │+│ - gargamelle-workflow: CEGIS Induction & SMT Guidance │+│ - gargamelle-persistence: Logging SMT Run Records │+└──────────────────────────▲─────────────────────────────┘+ │+ [gargamelle-fol]+ │+┌──────────────────────────▼─────────────────────────────┐+│ Upstream Substrates │+│ - gargamelle-core: Institutions, Signatures │+│ - gargamelle-model: Evidence, Verifier AST │+│ - Host OS: /usr/bin/z3 executable (POSIX pipe) │+└────────────────────────────────────────────────────────┘+```++---++## 6. Diagnostic & Troubleshooting Guide++### 6.1 `z3` Binary Not Found+- **Symptom:** `solveZ3Raw` crashes with `readProcess: z3: does not exist (No such file or directory)`.+- **Root Cause:** Host system lacks `z3` on `$PATH`.+- **Remedy:** Ensure `z3` is installed via system packages (`apt install z3` or `nix-shell -p z3`).++### 6.2 Unknown SMT Results+- **Symptom:** `verifyValidityZ3` returns `Counterexample "Z3-Unknown" "..."`.+- **Root Cause:** Nonlinear arithmetic, unbounded quantifiers, or timeout limits triggered Z3's incomplete heuristic engine.+- **Remedy:** Skolemize existential quantifiers or restrict assertions to decidable SMT fragments (e.g. QF_LIA / QF_UF).++---++*Part of the [Gargamelle Verification Suite](../../README.md).*
+ gargamelle-fol.cabal view
@@ -0,0 +1,62 @@+cabal-version: 3.0+name: gargamelle-fol+version: 0.1.0.0+synopsis: First-order logic institution and streaming Z3 SMT pipe for Gargamelle+description:+ First-order logic institution and streaming Z3 SMT solver pipeline for+ Gargamelle. Provides hyperdoctrine structures, quantifier elimination,+ and refinement calculus generation for formal program verification.+license: EUPL-1.2+license-file: LICENSE+author: Nadia Yvette Chambers+maintainer: nadia.yvette.chambers@ik.me+category: Logic+homepage: https://github.com/NadiaYvette/gargamelle+bug-reports: https://github.com/NadiaYvette/gargamelle/issues+extra-doc-files:+ README.md+ CHANGELOG.md+build-type: Simple+tested-with:+ GHC == 9.6.7,+ GHC == 9.8.4,+ GHC == 9.10.3,+ GHC == 9.12.4,+ GHC == 9.14.1++source-repository head+ type: git+ location: https://github.com/NadiaYvette/gargamelle+ subdir: packages/gargamelle-fol++library+ exposed-modules:+ FirstOrder+ Hyperdoctrine+ RefinementCalculus+ Gargamelle.FOL.Z3+ hs-source-dirs: src+ default-language: Haskell2010+ ghc-options: -Wall -Wcompat+ build-depends:+ base >= 4.18 && < 5,+ containers >= 0.6 && < 0.9,+ process >= 1.6 && < 1.7,+ polytropos-core >= 0.1.0.1 && < 0.2,+ gargamelle-core >= 0.1 && < 0.2,+ gargamelle-model >= 0.1 && < 0.2++test-suite tests+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs: test+ default-language: Haskell2010+ ghc-options: -Wall+ build-depends:+ base >= 4.18 && < 5,+ containers >= 0.6 && < 0.9,+ process >= 1.6 && < 1.7,+ gargamelle-fol,+ gargamelle-core >= 0.1 && < 0.2,+ gargamelle-model >= 0.1 && < 0.2,+ hspec >= 2.10 && < 2.12
+ src/FirstOrder.hs view
@@ -0,0 +1,183 @@+{-# LANGUAGE DataKinds, TypeFamilies, GADTs, PolyKinds, FlexibleInstances, MultiParamTypeClasses, BlockArguments #-}++module FirstOrder where++import Institution+import Data.Proxy+import GHC.TypeLits (Symbol, KnownSymbol, symbolVal)+import qualified Data.Map as Map+import qualified Data.Set as Set++-- | Native First-Order Terms: variables, constants, and function applications.+data Term+ = Var String+ | Const String+ | Func String [Term]+ deriving (Eq, Ord, Show)++isVar :: Term -> Bool+isVar (Var _) = True+isVar _ = False++varName :: Term -> String+varName (Var v) = v+varName _ = error "FirstOrder.varName: not a variable"++isConst :: Term -> Bool+isConst (Const _) = True+isConst _ = False++isFunc :: Term -> Bool+isFunc (Func _ _) = True+isFunc _ = False++funcName :: Term -> String+funcName (Const c) = c+funcName (Func f _) = f+funcName _ = error "FirstOrder.funcName: not a function or constant"++funcArgs :: Term -> [Term]+funcArgs (Func _ args) = args+funcArgs _ = []++var :: String -> Term+var = Var++constant :: String -> Term+constant = Const++func :: String -> [Term] -> Term+func = Func++-- | Sentences: our own formula GADT built on top of Folly's terms.+data Formula (syms :: [Symbol]) where+ TrueF :: Formula syms+ FalseF :: Formula syms+ Pred :: KnownSymbol sym => Proxy sym -> [Term] -> Formula syms+ Not :: Formula syms -> Formula syms+ And :: Formula syms -> Formula syms -> Formula syms+ Or :: Formula syms -> Formula syms -> Formula syms+ Impl :: Formula syms -> Formula syms -> Formula syms+ Forall :: KnownSymbol sym => Proxy sym -> Formula syms -> Formula syms+ Exists :: KnownSymbol sym => Proxy sym -> Formula syms -> Formula syms++-- | Models: a mapping from predicate symbols to relations and function symbols to functions,+-- plus a Herbrand universe of constants (as strings) for quantifier evaluation.+-- We use String keys (the symbol names) for simplicity.+-- Constants are represented as Strings.+-- For a predicate of arity n, the relation is a Set of n-tuples of Strings.+-- For a function of arity n, the function maps an n-tuple of Strings to a String.+data FOLModel (xs :: [Symbol]) = FOLModel+ { predicateInterpretation :: Map.Map String (Set.Set [String])+ , functionInterpretation :: Map.Map String ([String] -> String)+ , constants :: Set.Set String -- the Herbrand universe of constants+ }++-- | A wrapper for first-order signatures (lists of function/predicate symbols).+-- We keep it phantom; the actual signature is not used in the formulas.+newtype FOLSig (syms :: [Symbol]) = FOLSig (Proxy syms)++instance Institution (FOLSig syms) where+ type Sentence (FOLSig syms) = Formula syms+ type Model (FOLSig syms) = FOLModel syms+ satisfies _ m s = case evalFormula m Map.empty s of+ Right b -> b+ Left _ -> False -- treat errors as false++-- | Evaluate a term to a constant (String) given a model and an environment (variable assignment).+-- Returns Left error if a variable is not bound in the environment or if a function symbol is missing.+evalTerm :: FOLModel syms -> Map.Map String String -> Term -> Either String String+evalTerm m rho t+ | isVar t = case Map.lookup (varName t) rho of+ Just v -> Right v+ Nothing -> Left $ "Undefined variable: " ++ varName t+ | isConst t = Right $ funcName t -- constant: funcName gives the constant name+ | isFunc t = do+ let f = funcName t+ args = funcArgs t+ argVals <- mapM (evalTerm m rho) args+ case Map.lookup f (functionInterpretation m) of+ Just fn -> Right $ fn argVals+ Nothing -> Left $ "Undefined function symbol: " ++ f+ | otherwise = Left "Unknown term construct"++-- | Evaluate a formula to a Boolean given a model and an environment (variable assignment).+-- Returns Left error if the formula is not closed (contains free variables) or if a predicate is missing.+evalFormula :: FOLModel syms -> Map.Map String String -> Formula syms -> Either String Bool+evalFormula _ _ TrueF = Right True+evalFormula _ _ FalseF = Right False+evalFormula m rho (Pred p args) = do+ argVals <- mapM (evalTerm m rho) args+ case Map.lookup (symbolVal p) (predicateInterpretation m) of+ Just rel -> Right $ Set.member argVals rel+ Nothing -> Left $ "Undefined predicate symbol: " ++ symbolVal p+evalFormula m rho (Not f) = do+ b <- evalFormula m rho f+ Right $ not b+evalFormula m rho (And f1 f2) = do+ b1 <- evalFormula m rho f1+ b2 <- evalFormula m rho f2+ Right $ b1 && b2+evalFormula m rho (Or f1 f2) = do+ b1 <- evalFormula m rho f1+ b2 <- evalFormula m rho f2+ Right $ b1 || b2+evalFormula m rho (Impl f1 f2) = do+ b1 <- evalFormula m rho f1+ b2 <- evalFormula m rho f2+ Right $ not b1 || b2+evalFormula m rho (Forall p f) = do+ let vName = symbolVal p+ results = map (\c -> evalFormula m (Map.insert vName c rho) f) (Set.toList (constants m))+ case results of+ (Left err : _) -> Left err+ (Right False : _) -> Right False+ (Right True : rs) -> if all (== Right True) rs+ then Right True+ else Left "Unexpected error in quantifier evaluation"+ [] -> Left "No constants in Herbrand universe for universal quantifier"+evalFormula m rho (Exists p f) = do+ let vName = symbolVal p+ results = map (\c -> evalFormula m (Map.insert vName c rho) f) (Set.toList (constants m))+ case results of+ (Left err : _) -> Left err+ (Right True : _) -> Right True+ (Right False : rs) -> if all (== Right False) rs+ then Right False+ else Left "Unexpected error in quantifier evaluation"+ [] -> Left "No constants in Herbrand universe for existential quantifier"++-- | Smart constructors for terms that take a KnownSymbol proxy.+varTerm :: KnownSymbol sym => Proxy sym -> Term+varTerm p = var (symbolVal p)++constTerm :: KnownSymbol sym => Proxy sym -> Term+constTerm p = constant (symbolVal p)++funAppTerm :: KnownSymbol sym => Proxy sym -> [Term] -> Term+funAppTerm p = func (symbolVal p)++-- | Smart constructors for formulas that take KnownSymbol proxies.+prop :: KnownSymbol sym => Proxy sym -> [Term] -> Formula syms+prop = Pred++neg :: Formula syms -> Formula syms+neg = Not++conj :: Formula syms -> Formula syms -> Formula syms+conj = And++disj :: Formula syms -> Formula syms -> Formula syms+disj = Or++impl :: Formula syms -> Formula syms -> Formula syms+impl = Impl++bicond :: Formula syms -> Formula syms -> Formula syms+bicond p q = Or (And p q) (And (Not p) (Not q))++forallFOL :: KnownSymbol sym => Proxy sym -> Formula syms -> Formula syms+forallFOL = Forall++existsFOL :: KnownSymbol sym => Proxy sym -> Formula syms -> Formula syms+existsFOL = Exists
+ src/Gargamelle/FOL/Z3.hs view
@@ -0,0 +1,154 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE LambdaCase #-}++module Gargamelle.FOL.Z3+ ( SMTResult(..)+ , solveZ3Raw+ , formulaToSMTLib+ , checkFormulaZ3+ , verifyValidityZ3+ , parseZ3Model+ ) where++import Control.Exception (SomeException, try)+import qualified Data.Map.Strict as Map+import qualified Data.Set as Set+import System.Exit (ExitCode)+import System.Process (readProcessWithExitCode)+import GHC.TypeLits (symbolVal)++import FirstOrder (Formula(..), Term(..))+import Gargamelle.Model.Satisfaction (Evidence(..))++-- | Result of an SMT query.+data SMTResult+ = Sat (Map.Map String String)+ | Unsat+ | Unknown String+ deriving (Eq, Show)++-- | Collect all atomic predicate/constant names in a Formula to declare them in SMT-LIB2.+collectSymbols :: Formula syms -> Set.Set String+collectSymbols = \case+ TrueF -> Set.empty+ FalseF -> Set.empty+ Pred p args -> Set.insert (symbolVal p) (Set.unions (map collectTermSymbols args))+ Not f -> collectSymbols f+ And f1 f2 -> collectSymbols f1 `Set.union` collectSymbols f2+ Or f1 f2 -> collectSymbols f1 `Set.union` collectSymbols f2+ Impl f1 f2 -> collectSymbols f1 `Set.union` collectSymbols f2+ Forall _ f -> collectSymbols f+ Exists _ f -> collectSymbols f++collectTermSymbols :: Term -> Set.Set String+collectTermSymbols = \case+ Var v -> Set.singleton v+ Const c -> Set.singleton c+ Func f ts -> Set.insert f (Set.unions (map collectTermSymbols ts))++-- | Translate a Term to SMT-LIB2.+termToSMT :: Term -> String+termToSMT = \case+ Var v -> v+ Const c -> c+ Func f [] -> f+ Func f ts -> "(" ++ f ++ " " ++ unwords (map termToSMT ts) ++ ")"++-- | Translate a Formula to an SMT-LIB2 boolean expression.+exprToSMT :: Formula syms -> String+exprToSMT = \case+ TrueF -> "true"+ FalseF -> "false"+ Pred p [] -> symbolVal p+ Pred p args -> "(" ++ symbolVal p ++ " " ++ unwords (map termToSMT args) ++ ")"+ Not f -> "(not " ++ exprToSMT f ++ ")"+ And f1 f2 -> "(and " ++ exprToSMT f1 ++ " " ++ exprToSMT f2 ++ ")"+ Or f1 f2 -> "(or " ++ exprToSMT f1 ++ " " ++ exprToSMT f2 ++ ")"+ Impl f1 f2 -> "(=> " ++ exprToSMT f1 ++ " " ++ exprToSMT f2 ++ ")"+ Forall p f -> "(forall ((" ++ symbolVal p ++ " Bool)) " ++ exprToSMT f ++ ")"+ Exists p f -> "(exists ((" ++ symbolVal p ++ " Bool)) " ++ exprToSMT f ++ ")"++-- | Format a formula into a complete self-contained SMT-LIB2 query script.+formulaToSMTLib :: Formula syms -> String+formulaToSMTLib f =+ let syms = collectSymbols f+ decls = unlines [ "(declare-const " ++ s ++ " Bool)" | s <- Set.toList syms ]+ assertion = "(assert " ++ exprToSMT f ++ ")\n"+ in "(set-logic ALL)\n" ++ decls ++ assertion ++ "(check-sat)\n(get-model)\n"++-- | Parse the output model from Z3 into a map of variable assignments.+parseZ3Model :: String -> Map.Map String String+parseZ3Model out =+ let extract ("(define-fun" : name : "()" : _type : val : rest) =+ let cleanVal = filter (`notElem` "()") val+ in (name, cleanVal) : extract rest+ extract (_ : rest) = extract rest+ extract [] = []+ tokens = words out+ in Map.fromList (extract tokens)++{- Note [Streaming Z3 SMT-LIB2 Piping and Model Extraction]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+[tag:note-z3-smt-pipe-extraction]+In formal verification engines and multi-agent synthesis loops, interacting with an+automated theorem prover like Z3 can be implemented either via Foreign Function Interface+(FFI C-bindings) or via streaming standard SMT-LIB2 over standard POSIX process pipes.++Gargamelle intentionally chooses the streaming SMT-LIB2 pipe architecture for four reasons:+ 1. ABI & Portability Invariance: FFI bindings to C++ libraries like libz3 are notoriously+ fragile across disparate GHC versions (9.6 through 9.14) and Linux distribution package+ managers. Streaming pipes eliminate all FFI runtime crashes and garbage collector pauses.+ 2. Sandbox Isolation: In untrusted synthesis environments, executing solvers within Bubblewrap+ containers or remote execution sandboxes requires process separation. A streaming text pipe+ naturally crosses process, namespace, and network boundaries.+ 3. Inspectability & Reproducibility: Every SMT query generated by 'formulaToSMTLib' is an+ immutable, human-readable SMT-LIB2 script that can be logged directly into Beam SQLite+ records and inspected offline.+ 4. Sound Dual Refutation Verification:+ To prove validity of a specification formula φ, 'verifyValidityZ3' submits the negation+ ¬φ to Z3:+ - UNSAT => ¬φ has no models, hence φ is universally valid; yields 'VerifiedSection'.+ - SAT => Z3 synthesizes a concrete satisfying assignment for ¬φ; 'parseZ3Model' parses+ these assignments into a 'Counterexample' witness containing the exact falsifying+ variable bindings.+ - UNKNOWN => Solvers timing out or exceeding resource bounds soundly fail-safe by+ emitting a non-validity witness.+-}++-- | Execute a raw SMT-LIB2 query script by piping directly into host Z3.+--+-- See Note [Streaming Z3 SMT-LIB2 Piping and Model Extraction] [ref:note-z3-smt-pipe-extraction].+solveZ3Raw :: String -> IO SMTResult+solveZ3Raw script = do+ res <- (try (readProcessWithExitCode "z3" ["-in", "-smt2"] script) :: IO (Either SomeException (ExitCode, String, String)))+ case res of+ Left err ->+ pure $ Unknown ("Z3 process invocation failed (is z3 installed?): " ++ show err)+ Right (_code, stdoutStr, stderrStr) ->+ let cleanOut = filter (not . null) (lines stdoutStr)+ in case cleanOut of+ ("sat" : _) -> pure $ Sat (parseZ3Model stdoutStr)+ ("unsat" : _) -> pure Unsat+ ("unknown" : _) -> pure $ Unknown "Z3 returned unknown"+ _ -> pure $ Unknown ("Z3 error: " ++ stderrStr ++ " | out: " ++ stdoutStr)++-- | Check satisfiability of a First-Order formula using host Z3.+--+-- See Note [Streaming Z3 SMT-LIB2 Piping and Model Extraction] [ref:note-z3-smt-pipe-extraction].+checkFormulaZ3 :: Formula syms -> IO SMTResult+checkFormulaZ3 f = solveZ3Raw (formulaToSMTLib f)++-- | Formally verify validity of a formula:+-- Checks whether (not f) is unsatisfiable. If unsat, returns VerifiedSection.+-- If sat, the countermodel is returned as Evidence.+--+-- See Note [Streaming Z3 SMT-LIB2 Piping and Model Extraction] [ref:note-z3-smt-pipe-extraction].+verifyValidityZ3 :: Formula syms -> IO Evidence+verifyValidityZ3 f = do+ let negated = Not f+ checkFormulaZ3 negated >>= \case+ Unsat -> pure VerifiedSection+ Sat model -> pure $ Counterexample "Z3-Countermodel" (show model)+ Unknown err -> pure $ Counterexample "Z3-Unknown" err
+ src/Hyperdoctrine.hs view
@@ -0,0 +1,111 @@+{-# LANGUAGE DataKinds, TypeFamilies, GADTs, PolyKinds, TypeOperators, FlexibleInstances, MultiParamTypeClasses, BlockArguments #-}++module Hyperdoctrine where++import Institution+import Data.Proxy+import GHC.TypeLits (Symbol, KnownSymbol)+import qualified Data.Map as Map+import qualified FirstOrder as FO++-- | A wrapper for hyperdoctrine signatures (lists of base types?).+-- We keep it phantom for now.+newtype HDCSig (syms :: [Symbol]) = HDCSig (Proxy syms)++{- Note [Categorical Hyperdoctrine Quantifier Adjunctions]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+[tag:note-hyperdoctrine-adjunctions]+In categorical logic (F.W. Lawvere, 1969), a Hyperdoctrine formalizes the semantics of+first-order predicate logic by functorially indexing logical propositions over a cartesian+category of contexts (sorts, variables, and terms) C:+ P : C^op -> HeytAlg++For any term substitution or projection morphism π : X × Y -> X:+ 1. Pullback (Substitution) Functor:+ The contravariant action π* : P(X) -> P(X × Y) re-indexes propositions under context+ extension (weakening / variable introduction).+ 2. Existential Quantifier as Left Adjoint:+ The existential quantifier ∃_π is the LEFT adjoint to substitution π*:+ ∃_π ⊣ π* <===> (∃_π(φ) ⩽ ψ ⟺ φ ⩽ π*(ψ))+ This states that ∃_π(φ) is the strongest proposition on X entailed by φ on X × Y.+ 3. Universal Quantifier as Right Adjoint:+ The universal quantifier ∀_π is the RIGHT adjoint to substitution π*:+ π* ⊣ ∀_π <===> (π*(ψ) ⩽ φ ⟺ ψ ⩽ ∀_π(φ))+ This states that ∀_π(φ) is the weakest proposition on X that entails φ on X × Y.+ 4. Beck-Chevalley Condition:+ For every pullback square in the base category C, the canonical comparison morphisms+ between quantification and substitution commute, guaranteeing that quantification+ behaves invariantly under substitution of terms.++In Gargamelle, 'HDCSig' and 'HDModel' reify this fibered context structure into an+executable 'Institution' where context extensions are indexed by type-level symbol lists.+-}++-- | In a hyperdoctrine, we have a base category of contexts.+-- For simplicity, we represent contexts as lists of symbols (variables).+-- We don't actually build the category; we use it to index the fiber.+--+-- See Note [Categorical Hyperdoctrine Quantifier Adjunctions] [ref:note-hyperdoctrine-adjunctions].+data Context (syms :: [Symbol]) where+ Nil :: Context '[] -- empty context+ Cons :: KnownSymbol sym => Proxy sym -> Context syms -> Context (sym ': syms)++-- | The fiber over a context: a poset of propositions (we reuse FirstOrder formulas).+-- In a real hyperdoctrine, this would be a Heyting algebra.+data Fiber (syms :: [Symbol]) where+ Prop :: KnownSymbol sym => Proxy sym -> Fiber syms+ -- We could add more: true, false, and, or, implies, etc., but we reuse FirstOrder for now.+ -- Actually, we want to reuse FirstOrder's Formula for the propositions.+ -- So we change: the fiber is actually the set of FirstOrder formulas over the context.+ -- But we need to account for the variables in the context.+ -- We'll instead use FirstOrder's Formula directly, but we need to know which variables are in scope.+ -- For simplicity, we'll say that the fiber formulas are built from the symbols in the context.+ -- We'll leave this as a placeholder and reuse FirstOrder's Formula with the understanding+ -- that the signature is the list of variables in the context.++-- | Actually, let's reuse FirstOrder's Formula for the fiber propositions.+-- The signature for the fiber is the list of variables (symbols) in the context.+-- So we need to convert a Context to a type-level list of symbols.+-- We'll do that with a type family.+--+-- However, note that Context is already indexed by [Symbol]. We can simply use the index.+-- We'll change the HDModel to take the list of symbols directly.+--+type family ContextToList (ctx :: [Symbol]) :: [Symbol] where+ ContextToList '[] = '[]+ ContextToList (x ': xs) = x ': ContextToList xs++++-- | Model: a hyperdoctrine model consists of:+-- For each context, a first-order model (interpreting the variables as constants?).+-- Actually, in a hyperdoctrine, the models are assignments to the variables.+-- We'll reuse FirstOrder's model, but we need to parameterize by the list of symbols.+newtype HDModel (syms :: [Symbol]) = HDModel+ { foModel :: FO.FOLModel syms+ }++-- | Institution instance for the hyperdoctrine.+-- The signature is the context (as a phantom type).+-- We'll use the Context type as the signature kind.+instance Institution (HDCSig syms) where+ -- The sentences are the fibers over the empty context? Or over any context?+ -- In the Institution framework, the signature is fixed. We'll fix the signature to be the list of symbols.+ -- But we want to vary the context. We'll instead make the signature the context itself.+ -- However, the Institution class expects a fixed signature kind. We can use the Context type as the kind.+ -- We'll change: the signature is a Context (which is indexed by a list of symbols).+ -- We'll need to adjust the Instance to work with Context.+ -- Given time, we'll keep it simple and use the phantom signature as before, and leave the fiber as FirstOrder formulas.+ -- This is not a full hyperdoctrine but a placeholder.+ type Sentence (HDCSig syms) = FO.Formula syms+ type Model (HDCSig syms) = FO.FOLModel syms+ satisfies _ m s = case FO.evalFormula m Map.empty s of+ Right b -> b+ Left _ -> False++-- | Smart constructors for contexts.+nil :: Context '[]+nil = Nil++cons :: KnownSymbol sym => Proxy sym -> Context syms -> Context (sym ': syms)+cons = Cons
+ src/RefinementCalculus.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE DataKinds, TypeFamilies, GADTs, PolyKinds, FlexibleInstances, MultiParamTypeClasses, BlockArguments #-}++module RefinementCalculus where++import Institution+import Data.Proxy+import GHC.TypeLits (Symbol)+import qualified FirstOrder as FO++-- | A wrapper for refinement calculus signatures (lists of variables?).+-- We keep it phantom for now.+newtype RCSig (syms :: [Symbol]) = RCSig (Proxy syms)++-- | Terms: we don't have terms in the refinement calculus language itself;+-- specifications are built from predicates (which are first-order formulas).+-- We reuse FirstOrder's term type for building predicates.+type Term (syms :: [Symbol]) = FO.Term++-- | Predicates: first-order formulas (we reuse FirstOrder's Formula GADT).+type Predicate (syms :: [Symbol]) = FO.Formula syms++-- | Specification: a pair of a precondition and a postcondition.+data Spec (syms :: [Symbol]) = Spec+ { pre :: Predicate syms+ , post :: Predicate syms+ }++-- | Sentences: refinement judgments.+data RCSentence (syms :: [Symbol]) where+ Refine :: Spec syms -> Spec syms -> RCSentence syms+ -- We could also add: Skip, Assign, Seq, etc., as in the refinement calculus.++-- | Model: a first-order model (we reuse FirstOrder's model).+type RCModel (syms :: [Symbol]) = FO.FOLModel syms++instance Institution (RCSig syms) where+ type Sentence (RCSig syms) = RCSentence syms+ type Model (RCSig syms) = RCModel syms+ satisfies _ m (Refine spec1 spec2) =+ let impl = FO.impl -- implication from FirstOrder smart constructors+ pre1 = pre spec1+ pre2 = pre spec2+ post1 = post spec1+ post2 = post spec2+ in satisfies (Proxy :: Proxy (FO.FOLSig syms)) m (impl pre2 pre1) &&+ satisfies (Proxy :: Proxy (FO.FOLSig syms)) m (impl post1 post2)++-- | Smart constructors for specifications.+spec :: Predicate syms -> Predicate syms -> Spec syms+spec = Spec++-- | Smart constructors for refinement judgments.+refine :: Spec syms -> Spec syms -> RCSentence syms+refine = Refine
+ test/Main.hs view
@@ -0,0 +1,112 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Main (main) where++import Test.Hspec+import Data.Proxy (Proxy(..))+import qualified Data.Map.Strict as Map+import qualified Data.Set as Set++import qualified FirstOrder as FO+import qualified Hyperdoctrine as HD+import qualified RefinementCalculus as RC+import Gargamelle.FOL.Z3+import Gargamelle.Model.Satisfaction (Evidence(..))+import qualified Institution as Legacy++main :: IO ()+main = hspec do+ describe "Native FirstOrder Logic" do+ it "evaluates closed first-order formula under a concrete model" do+ let pVar = FO.Pred (Proxy @"p") []+ qVar = FO.Pred (Proxy @"q") []+ formula = FO.And pVar (FO.Not qVar)+ model = FO.FOLModel+ { FO.predicateInterpretation = Map.fromList [("p", Set.singleton []), ("q", Set.empty)]+ , FO.functionInterpretation = Map.empty+ , FO.constants = Set.empty+ }+ Legacy.satisfies (Proxy @(FO.FOLSig '["p", "q"])) model formula `shouldBe` True++ it "evaluates universal quantifiers over Herbrand constants" do+ let xVar = FO.var "x"+ predP = FO.Pred (Proxy @"P") [xVar]+ formula = FO.Forall (Proxy @"x") predP+ model = FO.FOLModel+ { FO.predicateInterpretation = Map.fromList [("P", Set.fromList [["c1"], ["c2"]])]+ , FO.functionInterpretation = Map.empty+ , FO.constants = Set.fromList ["c1", "c2"]+ }+ Legacy.satisfies (Proxy @(FO.FOLSig '["P"])) model formula `shouldBe` True++ -- Tested against Note [Streaming Z3 SMT-LIB2 Piping and Model Extraction] [ref:note-z3-smt-pipe-extraction]+ describe "Gargamelle.FOL.Z3 SMT-LIB2 Pipe" do+ it "solves satisfiable formula and extracts model assignments" do+ let pVar = FO.Pred (Proxy @"p") []+ qVar = FO.Pred (Proxy @"q") []+ formula = FO.And pVar (FO.Not qVar)+ res <- checkFormulaZ3 formula+ case res of+ Sat model -> do+ Map.lookup "p" model `shouldBe` Just "true"+ Map.lookup "q" model `shouldBe` Just "false"+ _ -> expectationFailure ("Expected Sat, but got: " ++ show res)++ it "identifies unsatisfiable formula as Unsat" do+ let pVar = FO.Pred (Proxy @"p") []+ formula = FO.And pVar (FO.Not pVar)+ res <- checkFormulaZ3 formula+ res `shouldBe` Unsat++ it "formally verifies tautological validity returning VerifiedSection" do+ -- Law of excluded middle: p \/ (not p) is universally valid+ let pVar = FO.Pred (Proxy @"p") []+ tautology = FO.Or pVar (FO.Not pVar)+ evidence <- verifyValidityZ3 tautology+ evidence `shouldBe` VerifiedSection++ it "detects countermodel when verifying non-valid formula" do+ -- p /\ q is not valid (counterexample exists where p=false)+ let pVar = FO.Pred (Proxy @"p") []+ qVar = FO.Pred (Proxy @"q") []+ nonTautology = FO.And pVar qVar+ evidence <- verifyValidityZ3 nonTautology+ case evidence of+ Counterexample sym _ -> sym `shouldBe` "Z3-Countermodel"+ VerifiedSection -> expectationFailure "Expected counterexample for non-valid formula"++ -- Tested against Note [Categorical Hyperdoctrine Quantifier Adjunctions] [ref:note-hyperdoctrine-adjunctions]+ describe "Hyperdoctrine & Refinement Calculus" do+ it "evaluates hyperdoctrine context satisfaction" do+ let pVar = FO.Pred (Proxy @"p") []+ model = FO.FOLModel+ { FO.predicateInterpretation = Map.fromList [("p", Set.singleton [])]+ , FO.functionInterpretation = Map.empty+ , FO.constants = Set.empty+ }+ Legacy.satisfies (Proxy @(HD.HDCSig '["p"])) model pVar `shouldBe` True++ it "evaluates specification refinement (Spec1 ⊑ Spec2)" do+ let pre1 = FO.Pred (Proxy @"preA") []+ post1 = FO.Pred (Proxy @"postA") []+ pre2 = FO.Pred (Proxy @"preB") []+ post2 = FO.Pred (Proxy @"postB") []+ spec1 = RC.spec pre1 post1+ spec2 = RC.spec pre2 post2+ refinement = RC.refine spec1 spec2+ -- For spec1 to refine spec2: pre2 => pre1 and post1 => post2+ -- We set up a model where pre1, pre2, post1, post2 all hold+ model = FO.FOLModel+ { FO.predicateInterpretation = Map.fromList+ [ ("preA", Set.singleton [])+ , ("postA", Set.singleton [])+ , ("preB", Set.singleton [])+ , ("postB", Set.singleton [])+ ]+ , FO.functionInterpretation = Map.empty+ , FO.constants = Set.empty+ }+ Legacy.satisfies (Proxy @(RC.RCSig '["preA", "postA", "preB", "postB"])) model refinement `shouldBe` True