Recent from talks
Contribute something
Nothing was collected or created yet.
Program synthesis
View on WikipediaIn computer science, program synthesis is the task to construct a program that provably satisfies a given high-level formal specification. In contrast to program verification, the program is to be constructed rather than given; however, both fields make use of formal proof techniques, and both comprise approaches of different degrees of automation. In contrast to automatic programming techniques, specifications in program synthesis are usually non-algorithmic statements in an appropriate logical calculus.[1]
The primary application of program synthesis is to relieve the programmer of the burden of writing correct, efficient code that satisfies a specification. However, program synthesis also has applications to superoptimization and inference of loop invariants.[2]
Origin
[edit]During the Summer Institute of Symbolic Logic at Cornell University in 1957, Alonzo Church defined the problem to synthesize a circuit from mathematical requirements.[3] Even though the work only refers to circuits and not programs, the work is considered to be one of the earliest descriptions of program synthesis and some researchers refer to program synthesis as "Church's Problem". In the 1960s, a similar idea for an "automatic programmer" was explored by researchers in artificial intelligence.[citation needed]
Since then, various research communities considered the problem of program synthesis. Notable works include the 1969 automata-theoretic approach by Büchi and Landweber,[4] and the works by Manna and Waldinger (c. 1980). The development of modern high-level programming languages can also be understood as a form of program synthesis.
21st century developments
[edit]This section needs expansion with: a more detailed overview of contemporary approaches. You can help by adding to it. (December 2022) |
The early 21st century has seen a surge of practical interest in the idea of program synthesis in the formal verification community and related fields. Armando Solar-Lezama showed that it is possible to encode program synthesis problems in Boolean logic and use algorithms for the Boolean satisfiability problem to automatically find programs.[5]
Syntax-guided synthesis
[edit]In 2013, a unified framework for program synthesis problems called Syntax-guided Synthesis (stylized SyGuS) was proposed by researchers at UPenn, UC Berkeley, and MIT.[6] The input to a SyGuS algorithm consists of a logical specification along with a context-free grammar of expressions that constrains the syntax of valid solutions.[7] For example, to synthesize a function f that returns the maximum of two integers, the logical specification might look like this:
(f(x,y) = x ∨ f(x,y) = y) ∧ f(x,y) ≥ x ∧ f(x,y) ≥ y
and the grammar might be:
<Exp> ::= x | y | 0 | 1 | <Exp> + <Exp> | ite(<Cond>, <Exp>, <Exp>)
<Cond> ::= <Exp> <= <Exp>
where "ite" stands for "if-then-else". The expression
ite(x <= y, y, x)
would be a valid solution, because it conforms to the grammar and the specification.
From 2014 through 2019, the yearly Syntax-Guided Synthesis Competition (or SyGuS-Comp) compared the different algorithms for program synthesis in a competitive event.[8] The competition used a standardized input format, SyGuS-IF, based on SMT-Lib 2. For example, the following SyGuS-IF encodes the problem of synthesizing the maximum of two integers (as presented above):
(set-logic LIA) (synth-fun f ((x Int) (y Int)) Int ((i Int) (c Int) (b Bool)) ((i Int (c x y (+ i i) (ite b i i))) (c Int (0 1)) (b Bool ((<= i i))))) (declare-var x Int) (declare-var y Int) (constraint (>= (f x y) x)) (constraint (>= (f x y) y)) (constraint (or (= (f x y) x) (= (f x y) y))) (check-synth)
A compliant solver might return the following output:
((define-fun f ((x Int) (y Int)) Int (ite (<= x y) y x)))
Counter-example guided inductive synthesis
[edit]Counter-example guided inductive synthesis (CEGIS) is an effective approach to building sound program synthesizers.[9][10] CEGIS involves the interplay of two components: a generator which generates candidate programs, and a verifier which checks whether the candidates satisfy the specification.
Given a set of inputs I, a set of possible programs P, and a specification S, the goal of program synthesis is to find a program p in P such that for all inputs i in I, S(p, i) holds. CEGIS is parameterized over a generator and a verifier:
- The generator takes a set of inputs T, and outputs a candidate program c that is correct on all the inputs in T, that is, a candidate such that for all inputs t in T, S(c, t) holds.
- The verifier takes a candidate program c and returns true if the program satisfies S on all inputs, and otherwise returns a counterexample, that is, an input e in I such that S(c, e) fails.
CEGIS runs the generator and verifier run in a loop, accumulating counter-examples:
algorithm cegis is
input: Program generator generate,
verifier verify,
specification spec,
output: Program that satisfies spec, or failure
inputs := empty set
loop
candidate := generate(spec, inputs)
if verify(spec, candidate) then
return candidate
else verify yields a counterexample e
add e to inputs
end if
Implementations of CEGIS typically use SMT solvers as verifiers.
CEGIS was inspired by counterexample-guided abstraction refinement (CEGAR).[11]
The framework of Manna and Waldinger
[edit]| Nr | Assertions | Goals | Program | Origin |
|---|---|---|---|---|
| 51 | ||||
| 52 | ||||
| 53 | s | |||
| 54 | t | |||
| 55 | Resolve(51,52) | |||
| 56 | s | Resolve(52,53) | ||
| 57 | s | Resolve(53,52) | ||
| 58 | p ? s : t | Resolve(53,54) |
The framework of Manna and Waldinger, published in 1980,[12][13] starts from a user-given first-order specification formula. For that formula, a proof is constructed, thereby also synthesizing a functional program from unifying substitutions.
The framework is presented in a table layout, the columns containing:
- A line number ("Nr") for reference purposes
- Formulas that already have been established, including axioms and preconditions, ("Assertions")
- Formulas still to be proven, including postconditions, ("Goals"),[note 1]
- Terms denoting a valid output value ("Program")[note 2]
- A justification for the current line ("Origin")
Initially, background knowledge, pre-conditions, and post-conditions are entered into the table. After that, appropriate proof rules are applied manually. The framework has been designed to enhance human readability of intermediate formulas: contrary to classical resolution, it does not require clausal normal form, but allows one to reason with formulas of arbitrary structure and containing any junctors ("non-clausal resolution"). The proof is complete when has been derived in the Goals column, or, equivalently, in the Assertions column. Programs obtained by this approach are guaranteed to satisfy the specification formula started from; in this sense they are correct by construction.[14] Only a minimalist, yet Turing-complete,[15] purely functional programming language, consisting of conditional, recursion, and arithmetic and other operators[note 3] is supported. Case studies performed within this framework synthesized algorithms to compute e.g. division, remainder,[16] square root,[17] term unification,[18] answers to relational database queries[19] and several sorting algorithms.[20][21]
Proof rules
[edit]Proof rules include:
- Non-clausal resolution (see table).
- For example, line 55 is obtained by resolving Assertion formulas from 51 and from 52 which both share some common subformula . The resolvent is formed as the disjunction of , with replaced by , and , with replaced by . This resolvent logically follows from the conjunction of and . More generally, and need to have only two unifiable subformulas and , respectively; their resolvent is then formed from and as before, where is the most general unifier of and . This rule generalizes resolution of clauses.[22]
- Program terms of parent formulas are combined as shown in line 58 to form the output of the resolvent. In the general case, is applied to the latter also. Since the subformula appears in the output, care must be taken to resolve only on subformulas corresponding to computable properties.
- Logical transformations.
- For example, can be transformed to ) in Assertions as well as in Goals, since both are equivalent.
- Splitting of conjunctive assertions and of disjunctive goals.
- An example is shown in lines 11 to 13 of the toy example below.
- This rule allows for synthesis of recursive functions. For a given pre- and postcondition "Given such that , find such that ", and an appropriate user-given well-ordering of the domain of , it is always sound to add an Assertion "".[23] Resolving with this assertion can introduce a recursive call to in the Program term.
- An example is given in Manna, Waldinger (1980), p.108-111, where an algorithm to compute quotient and remainder of two given integers is synthesized, using the well-order defined by (p.110).
Murray has shown these rules to be complete for first-order logic.[24] In 1986, Manna and Waldinger added generalized E-resolution and paramodulation rules to handle also equality;[25] later, these rules turned out to be incomplete (but nevertheless sound).[26]
Example
[edit]| Nr | Assertions | Goals | Program | Origin |
|---|---|---|---|---|
| 1 | Axiom | |||
| 2 | Axiom | |||
| 3 | Axiom | |||
| 10 | M | Specification | ||
| 11 | M | Distr(10) | ||
| 12 | M | Split(11) | ||
| 13 | M | Split(11) | ||
| 14 | x | Resolve(12,1) | ||
| 15 | x | Resolve(14,2) | ||
| 16 | x | Resolve(15,3) | ||
| 17 | y | Resolve(13,1) | ||
| 18 | y | Resolve(17,2) | ||
| 19 | x<y ? y : x | Resolve(18,16) |
As a toy example, a functional program to compute the maximum of two numbers and can be derived as follows.[citation needed]
Starting from the requirement description "The maximum is larger than or equal to any given number, and is one of the given numbers", the first-order formula is obtained as its formal translation. This formula is to be proved. By reverse Skolemization,[note 4] the specification in line 10 is obtained, an upper- and lower-case letter denoting a variable and a Skolem constant, respectively.
After applying a transformation rule for the distributive law in line 11, the proof goal is a disjunction, and hence can be split into two cases, viz. lines 12 and 13.
Turning to the first case, resolving line 12 with the axiom in line 1 leads to instantiation of the program variable in line 14. Intuitively, the last conjunct of line 12 prescribes the value that must take in this case. Formally, the non-clausal resolution rule shown in line 57 above is applied to lines 12 and 1, with
- p being the common instance x=x of A=A and x=M, obtained by syntactically unifying the latter formulas,
- F[p] being true ∧ x=x, obtained from instantiated line 1 (appropriately padded to make the context F[⋅] around p visible), and
- G[p] being x ≤ x ∧ y ≤ x ∧ x = x, obtained from instantiated line 12,
yielding true ∧ false) ∧ (x ≤ x ∧ y ≤ x ∧ true, which simplifies to .
In a similar way, line 14 yields line 15 and then line 16 by resolution. Also, the second case, in line 13, is handled similarly, yielding eventually line 18.
In a last step, both cases (i.e. lines 16 and 18) are joined, using the resolution rule from line 58; to make that rule applicable, the preparatory step 15→16 was needed. Intuitively, line 18 could be read as "in case , the output is valid (with respect to the original specification), while line 15 says "in case , the output is valid; the step 15→16 established that both cases 16 and 18 are complementary.[note 5] Since both line 16 and 18 comes with a program term, a conditional expression results in the program column. Since the goal formula has been derived, the proof is done, and the program column of the "" line contains the program.
See also
[edit]Notes
[edit]- ^ The distinction "Assertions" / "Goals" is for convenience only; following the paradigm of proof by contradiction, a Goal is equivalent to an assertion .
- ^ When and is the Goal formula and the Program term in a line, respectively, then in all cases where holds, is a valid output of the program to be synthesized. This invariant is maintained by all proof rules. An Assertion formula usually is not associated with a Program term.
- ^ Only the conditional operator (?:) is supported from the beginning. However, arbitrary new operators and relations can be added by providing their properties as axioms. In the toy example below, only the properties of and that are actually needed in the proof have been axiomatized, in line 1 to 3.
- ^ While ordinary Skolemization preserves satisfiability, reverse Skolemization, i.e. replacing universally quantified variables by functions, preserves validity.
- ^ Axiom 3 was needed for that; in fact, if wasn't a total order, no maximum could be computed for uncomparable inputs .
References
[edit]- ^ Basin, D.; Deville, Y.; Flener, P.; Hamfelt, A.; Fischer Nilsson, J. (2004). "Synthesis of programs in computational logic". In M. Bruynooghe and K.-K. Lau (ed.). Program Development in Computational Logic. LNCS. Vol. 3049. Springer. pp. 30–65. CiteSeerX 10.1.1.62.4976.
- ^ (Alur, Singh & Fisman)
- ^ Alonzo Church (1957). "Applications of recursive arithmetic to the problem of circuit synthesis". Summaries of the Summer Institute of Symbolic Logic. 1: 3–50.
- ^ Richard Büchi, Lawrence Landweber (Apr 1969). "Solving Sequential Conditions by Finite-State Strategies". Transactions of the American Mathematical Society. 138: 295–311. doi:10.2307/1994916. JSTOR 1994916.
- ^ (Solar-Lezama)
- ^ Alur, Rajeev; al., et (2013). "Syntax-guided Synthesis". Proceedings of Formal Methods in Computer-Aided Design. IEEE. p. 8.
- ^ (David & Kroening)
- ^ SyGuS-Comp (Syntax-Guided Synthesis Competition)
- ^ (Solar-Lezama)
- ^ (David & Kroening)
- ^ (Solar-Lezama)
- ^ Zohar Manna, Richard Waldinger (Jan 1980). "A Deductive Approach to Program Synthesis". ACM Transactions on Programming Languages and Systems. 2: 90–121. doi:10.1145/357084.357090. S2CID 14770735.
- ^ Zohar Manna and Richard Waldinger (Dec 1978). A Deductive Approach to Program Synthesis (PDF) (Technical Note). SRI International. Archived (PDF) from the original on January 27, 2021.
- ^ See Manna, Waldinger (1980), p.100 for correctness of the resolution rules.
- ^ Boyer, Robert S.; Moore, J. Strother (May 1983). A Mechanical Proof of the Turing Completeness of Pure Lisp (PDF) (Technical report). Institute for Computing Science, University of Texas at Austin. 37. Archived (PDF) from the original on 22 September 2017.
- ^ Manna, Waldinger (1980), p.108-111
- ^ Zohar Manna and Richard Waldinger (Aug 1987). "The Origin of a Binary-Search Paradigm". Science of Computer Programming. 9 (1): 37–83. doi:10.1016/0167-6423(87)90025-6.
- ^ Daniele Nardi (1989). "Formal Synthesis of a Unification Algorithm by the Deductive-Tableau Method". Journal of Logic Programming. 7: 1–43. doi:10.1016/0743-1066(89)90008-3.
- ^ Daniele Nardi and Riccardo Rosati (1992). "Deductive Synthesis of Programs for Query Answering". In Kung-Kiu Lau and Tim Clement (ed.). International Workshop on Logic Program Synthesis and Transformation (LOPSTR). Workshops in Computing. Springer. pp. 15–29. doi:10.1007/978-1-4471-3560-9_2. ISBN 978-3-540-19806-2.
- ^ Jonathan Traugott (1986). "Deductive Synthesis of Sorting Programs". Proceedings of the International Conference on Automated Deduction. LNCS. Vol. 230. Springer. pp. 641–660.
- ^ Jonathan Traugott (Jun 1989). "Deductive Synthesis of Sorting Programs". Journal of Symbolic Computation. 7 (6): 533–572. doi:10.1016/S0747-7171(89)80040-9.
- ^ Manna, Waldinger (1980), p.99
- ^ Manna, Waldinger (1980), p.104
- ^ Manna, Waldinger (1980), p.103, referring to: Neil V. Murray (Feb 1979). A Proof Procedure for Quantifier-Free Non-Clausal First Order Logic (Technical report). Syracuse Univ. 2-79.
- ^ Zohar Manna, Richard Waldinger (Jan 1986). "Special Relations in Automated Deduction". Journal of the ACM. 33: 1–59. doi:10.1145/4904.4905. S2CID 15140138.
- ^ Zohar Manna, Richard Waldinger (1992). "The Special-Relations Rules are Incomplete". Proc. CADE 11. LNCS. Vol. 607. Springer. pp. 492–506.
- David, Cristina; Kroening, Daniel (2017-10-13). "Program synthesis: challenges and opportunities". Philosophical Transactions of the Royal Society A: Mathematical, Physical and Engineering Sciences. 375 (2104) 20150403. Bibcode:2017RSPTA.37550403D. doi:10.1098/rsta.2015.0403. ISSN 1364-503X. PMC 5597726. PMID 28871052.
- Alur, Rajeev; Singh, Rishabh; Fisman, Dana; Solar-Lezama, Armando (2018-11-20). "Search-based program synthesis". Communications of the ACM. 61 (12): 84–93. doi:10.1145/3208071. ISSN 0001-0782.
- Zohar Manna, Richard Waldinger (1975). "Knowledge and Reasoning in Program Synthesis". Artificial Intelligence. 6 (2): 175–208. doi:10.1016/0004-3702(75)90008-9.
- Solar-Lezama, Armando (2008). Program synthesis by sketching (PDF) (Ph.D.). University of California, Berkeley.
Program synthesis
View on GrokipediaFundamentals
Definition and Goals
Program synthesis is the automated process of constructing a program in a given programming language that satisfies a high-level specification, such as logical formulas, input-output examples, or natural language descriptions.[5] This task, often described as the mechanized construction of software or "self-writing code," involves searching for implementations that meet user intent without requiring manual algorithmic design.[6] By treating the problem as a solver for existential second-order logic queries, synthesis bridges abstract requirements with executable code.[6] The primary goals of program synthesis include relieving programmers from low-level implementation details, ensuring the generated programs are correct with respect to the provided specifications, and accelerating software development workflows.[5] It aims to automate the discovery of programs within a defined search space, often using constraints to guide the process toward efficient and interpretable solutions.[2] Ultimately, synthesis seeks to enable non-experts, such as end-users in domains like data processing, to create functional software through intuitive inputs rather than traditional coding.[6] Key benefits encompass a significant reduction in programming errors by verifying outputs against specifications, faster prototyping through automated code generation, and the facilitation of domain-specific automation for tasks like spreadsheet manipulations or API integrations.[5] These advantages democratize software creation, lowering barriers for non-programmers and enhancing overall productivity in software engineering.[2]Types of Specifications
Program synthesis relies on diverse forms of specifications to guide the automatic generation of programs, each offering unique ways to articulate desired behaviors while imposing different requirements on users and synthesis systems. Formal specifications express the intended program behavior using logical formulas, typically in first-order logic or extensions like linear temporal logic, that define input-output relations or invariant properties. For example, a specification might require that a function sort(x) produces a permutation of input x where for all adjacent elements x ≤ x[i+1]. These specifications enable rigorous verification and deductive derivation of programs as proofs from axioms, ensuring completeness and correctness when the logic is fully axiomatized. Partial programs, often called sketches, provide structured incomplete code with placeholders (holes) that the synthesizer fills to realize the functionality, guided by auxiliary specifications like assertions or examples.[7] In systems like Sketch, a programmer might supply a template such asint max(int a, int b) { return a > b ? a : ??; }, where the hole ?? is resolved to b while satisfying a maximality property.[7] This format incorporates high-level algorithmic insight, narrowing the synthesis search space and improving efficiency for complex tasks.[8]
Input-output examples form the basis of programming by example (PBE), where sets of concrete input-output pairs implicitly specify the program's mapping without explicit logic. For instance, in string processing, examples like input "John, Doe" → output "Doe, John" and "Alice Smith" → output "Smith, Alice" can synthesize a name-reversing function. Tools such as Microsoft's FlashFill leverage these to automate spreadsheet transformations, inferring general rules from few examples.[5]
Natural language descriptions capture user intent in prose, which synthesis systems parse via natural language processing to produce code in a domain-specific language.[9] A query like "compute the average of numbers in a list" might yield a summation and division expression after semantic mapping.[10] This approach, as in frameworks combining semantic parsing with search, supports intuitive specification but relies on robust NLP models for disambiguation.[9]
Additional formats for specifications include trace-based representations, which detail sequences of program states or execution paths to illustrate dynamic behavior, and oracle-based ones, employing black-box queries to an external verifier for validation without revealing internal logic.[11] Traces might specify a sorting algorithm via stepwise permutations of an array, while oracles provide yes/no answers to property checks.[11]
Each specification type involves trade-offs in precision, usability, and completeness: formal specifications offer unambiguous guarantees but require domain expertise and can be verbose; partial programs and input-output examples balance guidance with automation yet risk under-specification or multiple interpretations; natural language is highly accessible for non-experts but introduces ambiguity that demands advanced parsing.[12] These choices influence synthesis tractability, with concise yet strong specifications often prioritizing user intent over exhaustive detail.[12]
Historical Development
Early Foundations (1960s-1980s)
The origins of program synthesis trace back to the 1960s within the fields of artificial intelligence and automated theorem proving, where researchers explored deriving programs directly from formal specifications using logical inference methods. Cordell Green pioneered this approach by adapting resolution-based theorem proving to construct programs from declarative descriptions, demonstrating how logical deductions could yield executable code for simple tasks like question-answering systems.[13] This work, building on earlier resolution techniques introduced by J.A. Robinson in 1965, positioned program synthesis as a form of automated reasoning rather than manual coding.[14] In the 1970s, developments accelerated through DARPA-funded initiatives in AI, which supported research on inductive inference and early automated programming tools. These projects emphasized learning general rules from examples to infer programs, contrasting with purely deductive methods, and produced prototypes for domains like pattern recognition and simple algorithm generation.[15] Key milestones included the 1969 paper by Richard Waldinger and Robert C.T. Lee, which outlined an automated system called PROW for synthesizing programs via theorem proving, treating synthesis as proving the existence of a solution to a specification. Complementing this, Zohar Manna and Richard Waldinger's 1977 monograph "Synthesis: Dreams ⇒ Programs" formalized deductive strategies for deriving recursive programs, establishing foundational principles for transforming specifications into code using logical rules.[16] Early efforts grappled with computational limitations of the era, such as deriving recursive functions and managing data structures like lists or trees on hardware with minimal memory and processing power. Researchers addressed these by restricting specifications to first-order logic and employing heuristics to prune search spaces during proof construction, enabling synthesis of non-trivial examples like sorting algorithms despite exponential complexity risks.[17] By the 1980s, program synthesis began integrating with emerging paradigms like expert systems and logic programming, where languages such as Prolog facilitated declarative specification and automated execution, bridging theoretical deduction with practical knowledge representation. This transition laid groundwork for more domain-specific applications, though scalability remained a persistent hurdle.[18] The deductive framework advanced by Manna and Waldinger during this period would later influence broader methodologies in automated programming.The Manna-Waldinger Framework
The Manna-Waldinger framework, developed in the late 1970s, formalizes deductive program synthesis as a goal-directed theorem-proving process, where constructing a program is equivalent to proving a theorem derived from the input specification.[19] In this paradigm, synthesis reduces to theorem proving: the specification serves as the theorem to be established constructively, and the proof yields a program that satisfies it.[19] The approach employs a backward-chaining strategy within a tableau-based proof system, starting from the goal (the existential claim in the specification) and working toward known axioms or input facts.[20] At its core, the framework operates on specifications of the form , where is an input predicate defining the problem domain, and describes the desired input-output relation; the synthesized program computes a term such that holds whenever is true.[20] The proof proceeds by applying inference rules to reduce the goal to subgoals, ultimately extracting the program from the successful proof tree via program-forming rules.[19] Key proof rules include instantiation (substituting specific terms for variables to specialize a formula), conjunction introduction (splitting a conjunctive goal into parallel subgoals), and elimination rules for quantifiers and connectives.[20] For recursive programs, specialized rules derive iterative or recursive structures from inductive hypotheses, such as the "going-up" rule for increasing induction or "going-down" for decreasing, enabling the extraction of loops from proofs involving well-founded orderings.[19] A representative example illustrates the mechanics: synthesizing a program to compute the maximum of two integers and . The specification is the theorem , where the input predicate is implicitly true for all integers, and the output predicate ensures is the larger value.[20] Backward chaining begins with the goal formula and applies case analysis on the relation between and : if , instantiate to satisfy , (via the axiom with , ), and (trivially false but adjusted in conditional); otherwise, instantiate .[20] Conjunction introduction combines the conditions, and the proof tree yields the program via extraction:z := if x_1 ≥ x_2 then x_1 else x_2
z := if x_1 ≥ x_2 then x_1 else x_2
21st-Century Revival
The resurgence of program synthesis in the 21st century was primarily driven by significant improvements in automated theorem provers and satisfiability modulo theories (SMT) solvers, which enabled scalable deductive methods for generating programs from specifications.[21] A pivotal advancement was the release of the Z3 SMT solver in 2008 by Microsoft Research, which provided efficient handling of complex constraints and became a cornerstone for practical synthesis tools by supporting rapid exploration of program spaces. These solvers addressed longstanding scalability issues in earlier deductive frameworks, shifting focus from purely theoretical constructs to implementable systems capable of tackling real-world programming tasks.[21] Key milestones marked this revival, including the introduction of the Sketch system in 2005 by Armando Solar-Lezama, which pioneered partial program completion through sketching—allowing users to provide high-level structures while the synthesizer filled in details using constraint solving. Building on this, Sumit Gulwani's FlashFill in 2011 demonstrated practical end-user synthesis by automatically generating string-processing programs in Microsoft Excel from input-output examples, influencing the deployment of synthesis in commercial software. These developments emphasized a transition from abstract proofs to domain-specific languages (DSLs) tailored for accessibility, such as those for data manipulation and verification, making synthesis viable for non-expert users.[21] The field's momentum grew through standardized evaluations, notably the Syntax-Guided Synthesis (SyGuS) competitions launched in 2014, which provided benchmarks for comparing solvers on tasks like bit-vector manipulation and invariant generation, fostering innovations in syntax-constrained search.[22] In the 2020s, integration with large language models (LLMs) introduced hybrid approaches, combining neural generation with symbolic verification; for instance, works from 2023 onward explored prompt-based program synthesis to leverage LLMs for initial candidates refined by SMT solvers, enhancing robustness in general-purpose languages.[23] This evolution has positioned program synthesis as a hybrid paradigm bridging machine learning and formal methods.[24]Synthesis Techniques
Deductive Approaches
Deductive approaches to program synthesis derive programs directly from formal specifications through logical inference and theorem proving, employing a top-down refinement process that ensures the resulting code is complete and sound with respect to the given specification. This method treats synthesis as a theorem-proving task, where the specification is transformed step-by-step into an executable program via deduction rules, such as unification and transformation, while maintaining provable correctness by construction. The foundational paradigm, as outlined in the work of Manna and Waldinger, frames program synthesis as constructing a proof that the specification holds for the derived program, avoiding empirical search in favor of rigorous logical derivation.[25] In modern implementations, deductive synthesis leverages satisfiability modulo theories (SMT) solvers to automate the inference process. The algorithm encodes the formal specification—typically expressed in a decidable logic like linear integer arithmetic or bit-vector theory—as a satisfiability formula over program variables and structures. The SMT solver then determines if the formula is satisfiable and, if so, extracts a concrete program by interpreting the satisfying model as assignments to these variables, effectively realizing the specification without loops or recursion in the initial cases. For example, tools like CVC4 integrate deductive strategies within the SyGuS (Syntax-Guided Synthesis) competition framework to solve such encodings efficiently. This approach has been demonstrated in synthesizing loop-free programs, such as bit-vector manipulations, where the solver verifies and constructs straight-line code meeting input-output constraints.[26][27] Variations of deductive synthesis include refinement-based techniques, which progressively refine an abstract specification through successive approximations until an executable form is reached, often using polymorphic refinement types to handle recursion and data structures. Another variant involves proof-carrying code, where the synthesis process generates not only the program but also a compact proof of its correctness, embedded with the code for runtime or deployment verification, ensuring safety in untrusted environments. These methods extend the core deductive principle to more complex domains, such as heap-manipulating programs, by translating synthesis proofs into verifiable certificates. A key advantage is the formal guarantee of correctness: if the specification is satisfied by the proof, the extracted program meets it exactly. This can be expressed as follows, where the program is derived from a proof of the specification : Such extraction is standard in proof assistants and SMT-based tools, yielding verified implementations.[28][29] Representative examples illustrate the power of deductive approaches, such as synthesizing sorting algorithms from specifications requiring permutations to maintain order while using equality logic for comparisons. In one such derivation, a recursive quicksort variant is obtained by proving that partitioning and recursion preserve the permutation invariant, directly yielding correct code without testing. These examples highlight how deductive methods excel in domains with precise logical specifications, producing provably optimal solutions like linear-time selection algorithms under bounded assumptions.Inductive and Example-Based Methods
Inductive program synthesis aims to learn a hypothesis program that generalizes over a set of training examples , typically consisting of input-output pairs, by maintaining and narrowing a version space of candidate programs that are consistent with .[30] This bottom-up approach contrasts with deductive methods by inferring programs directly from data rather than deriving them from formal logical specifications, often using algorithms that symbolically represent and prune the space of possible programs to ensure efficiency.[31] Version space algorithms, originally from machine learning, play a central role here by intersecting generalizations and specializations of examples to converge on viable hypotheses.[30] A prominent paradigm within inductive synthesis is programming by example (PBE), where the input specification is a finite set of input-output pairs, and the synthesizer searches for a program—often in a domain-specific language (DSL)—that satisfies all pairs through generalization techniques.[32] For instance, given examples like transforming "hello world" to "Hello World" and "flash fill" to "Flash Fill", a PBE system might generalize to a rule capitalizing the first letter of each word, potentially extending to loops or conditionals for more complex patterns.[33] Examples thus act as partial specifications that guide synthesis without requiring exhaustive formal descriptions.[30] Key techniques in inductive synthesis include noise-tolerant induction, which handles imperfect or erroneous examples by adopting a probabilistic framework, such as using Bayes' theorem to compute posteriors over hypotheses based on likelihoods and priors.[34] Hypothesis ranking further refines this by scoring candidates, for example, via where accuracy measures fit to the examples and simplicity penalizes overly complex programs to favor generalizable solutions.[30] Early systems like IGOR, developed in the 2000s (IGOR I in 2006) and extended as IGOR2 in 2008, applied these ideas to equation discovery and functional program induction from input-output examples, particularly for tasks like number series prediction.[35] In the 2010s, Microsoft's PROSE framework advanced PBE for DSLs, enabling scalable synthesis in applications like string manipulation and data transformation by combining analytical generalization with search-based enumeration.[32] Despite these advances, inductive methods face limitations such as overfitting to the provided examples, which can produce programs that fail on unseen inputs, necessitating a diverse and sufficiently covering set of to ensure robustness.[30] Additionally, the combinatorial explosion in candidate programs often requires restricting the search space via DSLs, though this can limit expressiveness for broader domains.[31]Syntax-Guided Synthesis
Syntax-guided synthesis constrains the search for programs by specifying a grammar that defines the allowable syntactic structures over the target language's constructs, thereby pruning the space of candidate programs to those that adhere to predefined templates or tree forms. The mechanism involves defining a context-free grammar that generates a set of expressions or programs, where synthesis proceeds by instantiating "holes" or non-terminals in partial tree structures to satisfy a given logical specification over a background theory . Formally, the problem seeks an expression in the language such that is valid in , where is a hole in the specification. This syntactic guidance ensures that only structurally valid programs are considered, avoiding semantically irrelevant candidates.[36] Search strategies in syntax-guided synthesis typically enumerate or optimize over the possible syntactic forms generated by the grammar. Enumerative approaches systematically generate expressions in order of increasing size, using the grammar to guide expansion and pruning invalid branches via partial evaluation against the specification. Optimization-based methods encode the selection of sub-trees as constraints, often using SAT or SMT solvers to find satisfying assignments that correspond to valid program trees. For instance, the grammar can restrict operations to specific forms, such as linear expressions or conditional statements, enabling efficient exploration even for complex domains.[36] A key algorithmic paradigm reduces the synthesis problem to operations on weighted automata or MAX-SAT encodings, particularly for objectives beyond mere satisfiability. In extensions, the task is formulated as finding a tree in the grammar that minimizes a cost function , which may quantify syntactic properties like tree depth or node counts, solved via weighted tree automata that generalize finite-state acceptance to weighted acceptance. This reduction allows for quantitative optimization, such as minimizing circuit size in synthesized programs.[37] Representative examples include synthesizing bit-vector operations in the SyGuS benchmarks, where grammars define operations like bitwise AND, shifts, and arithmetic to realize efficient transformations such as population count or bit manipulation for low-level algorithms. Tools and frameworks from the SyGuS competition, such as those integrated with SMT solvers like CVC4, have demonstrated success on these benchmarks, often synthesizing concise implementations for bit-vector functions used in cryptographic primitives, as seen in optimizations for homomorphic encryption circuits. These applications highlight the technique's utility in domains requiring precise control over low-level operations. The primary benefit of syntax-guided synthesis is a dramatic reduction in search space explosion by enforcing syntactic constraints upfront, enabling scalability to larger grammars compared to unconstrained enumeration. In the late 2010s, extensions have incorporated probabilistic grammars, such as higher-order probabilistic models learned from prior solutions via transfer learning, to bias sampling toward high-likelihood programs and accelerate convergence in stochastic search. These advancements maintain the rule-based guidance of traditional syntax constraints while integrating data-driven priors for improved efficiency.[38]Counter-Example Guided Inductive Synthesis
Counter-example guided inductive synthesis (CEGIS) is an iterative algorithm for program synthesis that combines inductive learning from positive examples with refinement via counterexamples generated by an oracle to ensure completeness against a specification. The process begins by synthesizing a set of candidate programs that satisfy a collection of input-output examples derived from the specification. These candidates are then verified against the full specification using an oracle, such as an SMT solver, which either confirms correctness or produces a counterexample—an input where a candidate violates the specification. This counterexample is added to the set of examples, and the synthesis step generates new candidates that must satisfy all accumulated examples, repeating until a verified program is found.[36] The core of CEGIS can be formalized as a loop that maintains a candidate program and a growing set of examples :while not verified(P, spec):
CE = oracle.find_violation(P, spec)
E = E ∪ {CE}
P = synthesize(E, grammar)
while not verified(P, spec):
CE = oracle.find_violation(P, spec)
E = E ∪ {CE}
P = synthesize(E, grammar)
verified(P, spec) checks if satisfies the specification spec, oracle.find_violation returns a counterexample if it exists, and synthesize generates a new consistent with within a given grammar or search space. This loop guarantees termination under finite candidate spaces, as each counterexample eliminates invalid candidates, progressively narrowing the search.[36]
In implementations, CEGIS often distinguishes between an "angelic" oracle, which non-deterministically selects counterexamples to maximize learning progress (idealized for theoretical analysis), and realistic variants using deterministic oracles like decision procedures that provide concrete violations. The angelic model assumes the oracle chooses the most informative counterexample, potentially leading to faster convergence, but practical systems rely on sound verifiers such as SMT solvers to approximate this. A prominent example is the integration of CEGIS into the CVC4 theorem prover during the 2010s, where it enables synthesis of bit-vector programs and quantified invariants by encoding the synthesis problem as quantified formulas and using counterexample-guided quantifier instantiation to drive the inductive phase. This approach has been applied to tasks like generating loop-free arithmetic circuits and string manipulations, demonstrating scalability on benchmarks with up to hundreds of lines of code.
A representative application of CEGIS involves synthesizing loop invariants for an array summation program, where the specification requires computing the sum of array elements while handling edge cases. Initial examples might cover typical cases like non-empty arrays with positive integers, yielding a candidate invariant such as "the partial sum equals the sum of processed elements." Verification reveals a counterexample, such as an empty array, prompting refinement to include a base case like "sum is zero if no elements remain." Subsequent iterations address further violations, like arrays with negative values or overflow, until the invariant holds universally, as verified by the oracle. This process highlights CEGIS's strength in iteratively strengthening partial solutions through targeted feedback.[36]
Recent advancements extend CEGIS to hybrid systems incorporating large language models (LLMs) for oracle simulation or candidate generation, particularly as of 2025. In these approaches, an LLM acts as the inductive synthesizer, proposing programs from natural language specifications or examples, while a symbolic verifier provides counterexamples to guide refinement in the traditional CEGIS loop. For instance, zero-shot learning with LLMs has been used for program repair, where the model iteratively fixes code based on test failures as counterexamples, achieving higher success rates on real-world benchmarks compared to pure symbolic methods alone. This integration leverages LLMs' semantic understanding to handle complex, informal specifications, while retaining formal guarantees from the verification oracle.[39]
Learning-Based Methods
Learning-based methods in program synthesis leverage machine learning techniques, particularly neural networks, to generate programs from specifications by learning patterns from large code corpora. These approaches treat synthesis as a probabilistic generation task, where models are trained to maximize the likelihood of producing correct programs given inputs such as natural language descriptions or input-output examples. A core formulation involves training parameters θ to maximize the conditional likelihood P(program | specification; θ), often using maximum likelihood estimation during pre-training on vast datasets of code snippets. Inference then selects the program as argmax_program P(program | specification; θ), enabling direct generation without exhaustive symbolic search.[40][41] Transformer-based models exemplify neural synthesis, pre-trained on millions of code functions across languages like Python and Java to capture syntactic and semantic structures. For instance, models like CodeT5 employ an encoder-decoder architecture with identifier-aware denoising objectives, achieving state-of-the-art results in generating code from natural language, such as translating docstrings to Python functions (e.g., producing a histogram plotting function from "plot the distribution of results"). To handle ambiguity in specifications, these systems use beam search during decoding, maintaining multiple candidate sequences to explore diverse outputs while prioritizing high-likelihood paths. This pre-training on code corpora addresses scalability challenges for large programming languages by amortizing search costs across similar tasks.[40] Reinforcement learning variants enhance these methods by incorporating rewards based on specification satisfaction, iteratively improving synthesis policies. In systems like DreamCoder, a wake-sleep algorithm trains a neural search policy on solved tasks (wake phase) and imagined programs (sleep phase), rewarding programs that execute correctly on examples while expanding a domain-specific library through abstraction. This enables recursive invention of reusable components, such as list-processing primitives, boosting performance across domains like graphics and puzzles. Recent integrations with large language models (LLMs) from 2023 onward have advanced natural language to code synthesis through fine-tuning on specialized tasks. CodeT5+, an open encoder-decoder LLM, combines span denoising with contrastive learning on bimodal code-text data, outperforming prior models on benchmarks like HumanEval (35.0% pass@1 accuracy) after instruction tuning for synthesis. Similarly, GPT-based synthesizers, fine-tuned on code generation datasets, handle ambiguous specs by generating multiple candidates via sampling or beam search, scaling to complex Python functions from docstrings with pre-training on GitHub repositories. These advances mitigate earlier limitations in compositional generalization, enabling broader applicability in real-world programming scenarios.Applications and Impacts
End-User and Interactive Programming
Program synthesis has significantly empowered end-users—individuals without formal programming expertise—by enabling interactive tools that automate repetitive tasks through intuitive demonstrations and examples. These tools leverage programming by example (PBE) techniques, where users provide input-output pairs or demonstrations, and the system infers the underlying program. This approach lowers the barrier to computational automation, allowing non-experts in domains like data analysis and office productivity to create custom scripts without writing code.[42] A landmark example is FlashFill, introduced in Microsoft Excel 2013, which synthesizes string transformation programs from user-provided examples of data reformatting. For instance, a user might demonstrate extracting first names from a list of full names, and FlashFill generates the corresponding formula to apply across the dataset. This feature, powered by the PROSE framework, handles common spreadsheet tasks like parsing dates or concatenating strings, making it accessible for everyday data manipulation.[33] The PROSE (Program Synthesis using Examples) framework from Microsoft provides an API for developers to build custom interactive synthesizers tailored to specific domains, supporting stateful sessions for iterative refinement. It separates domain-specific language (DSL) design from general-purpose search algorithms, enabling rapid prototyping of end-user tools. For example, PROSE has been used to create synthesizers for table layout in PowerPoint or query generation in Excel, where users iteratively provide examples to guide the synthesis process. User studies on PROSE-based tools, such as those evaluating FlashFill-like interfaces, demonstrate significant productivity improvements, with end-users completing data transformation tasks up to several times faster than manual methods.[33][42] In end-user scenarios, program synthesis automates repetitive operations in spreadsheets, such as cleaning datasets or generating reports, and in graphical user interfaces (GUIs), where demonstrations capture user interactions to create macros. For regex synthesis, tools infer regular expressions from positive and negative string examples; a user might provide emails as positive inputs and non-emails as negatives, yielding a pattern like\w+@\w+\.\w+ without regex knowledge. Similarly, UI macro synthesis from demonstrations enables non-programmers to record actions like form filling or navigation sequences, generalizing them into reusable scripts for web or desktop applications. These capabilities are exemplified in systems like ParamMacros, which generalize UI automation from end-user traces.
Overall, interactive program synthesis democratizes access to automation, particularly in data analysis and administrative workflows, by shifting the focus from coding syntax to intent expression via examples. This reduces cognitive load for non-experts and fosters broader adoption of computational tools, with impacts seen in widespread use of features like FlashFill by millions of Excel users annually.[43][42]
