Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions plinth/app/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module Main (main) where

import Prelude

import Factorial (factorial10Code)
import Fibonacci (fibonacci25Code)
import PlutusCore.Pretty qualified as PP
import PlutusCore.Quote (runQuoteT)
Expand All @@ -12,6 +13,7 @@ import UntypedPlutusCore.DeBruijn (unDeBruijnTerm)
main :: IO ()
main = do
writeCodeToFile "fibonacci.uplc" fibonacci25Code
writeCodeToFile "factorial.uplc" factorial10Code

writeCodeToFile :: FilePath -> CompiledCode a -> IO ()
writeCodeToFile filePath code = do
Expand Down
46 changes: 46 additions & 0 deletions plinth/src/Factorial.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE Strict #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE NoImplicitPrelude #-}
--
{-# OPTIONS_GHC -fno-full-laziness #-}
{-# OPTIONS_GHC -fno-ignore-interface-pragmas #-}
{-# OPTIONS_GHC -fno-omit-interface-pragmas #-}
{-# OPTIONS_GHC -fno-spec-constr #-}
{-# OPTIONS_GHC -fno-specialise #-}
{-# OPTIONS_GHC -fno-strictness #-}
{-# OPTIONS_GHC -fno-unbox-small-strict-fields #-}
{-# OPTIONS_GHC -fno-unbox-strict-fields #-}
{-# OPTIONS_GHC -fplugin PlutusTx.Plugin #-}
{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:no-conservative-optimisation #-}
Comment thread
Unisay marked this conversation as resolved.
{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:no-preserve-logging #-}
{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:remove-trace #-}
{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:target-version=1.1.0 #-}

module Factorial (factorialCode, factorial10Code) where

import PlutusTx
import PlutusTx.Prelude

-- | Compiled validator script
factorialCode :: CompiledCode (Integer -> Integer)
factorialCode = $$(PlutusTx.compile [||factorial||])

-- | The compiled factorial validator for n=10
factorial10Code :: CompiledCode Integer
factorial10Code = factorialCode `unsafeApplyCode` liftCodeDef (10 :: Integer)

{-# INLINEABLE factorial #-}
factorial :: Integer -> Integer
factorial n
| n <= 0 = 1
| otherwise = n * factorial (n - 1)
4 changes: 3 additions & 1 deletion plinth/uplc-cape-benchmarks.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ common common-language

library
import: common-language
exposed-modules: Fibonacci
exposed-modules:
Factorial
Fibonacci

-- Future benchmark modules:
-- TwoPartyEscrow
Expand Down
173 changes: 173 additions & 0 deletions scenarios/factorial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# Factorial Benchmark Scenario

The Factorial benchmark is a **synthetic computational scenario** designed to measure the performance characteristics of iterative and recursive algorithms implemented as UPLC programs. This benchmark tests a compiler's ability to optimize mathematical computations, manage stack operations, and handle integer arithmetic efficiently.

## TL;DR

Implement a Factorial function that computes **factorial(10) = 3628800** and compile it as a fully-applied UPLC program.

**Required Files**: Submit `factorial.uplc`, `metadata.json`, `metrics.json` to `submissions/factorial/{Compiler}_{Version}_{Handle}/`

**Target**: `factorial(10)` → Expected result: `3628800`
**Metrics**: CPU units, Memory units, Script size (bytes), Term size
**Constraints**: Plutus Core 1.1.0, Plutus V3 recommended, CEK machine budget limits
**Implementation**: Choose recursive or iterative approach

---

## Exact Task

Implement a Factorial function and compile it as a **fully-applied UPLC program** that computes the factorial of 10.

### Core Requirements

1. **Function Implementation**: Create a function that computes factorials using the mathematical definition:

- `factorial(0) = 1`
- `factorial(n) = n * factorial(n-1)` for n > 0

2. **Full Application**: The UPLC program must be fully-applied with the target value (10) baked in during compilation, not passed as a parameter.

3. **Target Computation**: `factorial(10)` must produce exactly `3628800`

### Implementation Approaches

Choose the approach that works best for your compiler:

#### Recursive Implementation (Most Direct)

```pseudocode
function factorial_recursive(n):
if n == 0:
return 1
return n * factorial_recursive(n - 1)
```

#### Iterative Implementation (More Efficient)

```pseudocode
function factorial_iterative(n):
if n == 0:
return 1

result = 1
for i in range(1, n + 1):
result = result * i

return result
```

---

## Acceptance Criteria

Your submission passes if:

- ✅ **Correctness**: Program outputs exactly `3628800`
- ✅ **Budget Compliance**: Executes within CEK machine CPU and memory limits
- ✅ **Determinism**: Produces identical results across multiple executions
- ✅ **Self-Contained**: No external dependencies or parameters
- ✅ **File Format**: Valid UPLC program that can be executed by the CEK evaluator

---

## Metrics Recorded

All submissions are measured on these standardized metrics:

| Metric | Description | Purpose |
| --- | --- | --- |
| **CPU Units** | Total execution units consumed | Computational efficiency |
| **Memory Units** | Peak memory usage during execution | Memory efficiency |
| **Script Size** | Compiled UPLC script size in bytes | Code generation efficiency |
| **Term Size** | UPLC term representation size | Optimization effectiveness |

**Measurement Environment**: Standard CEK machine evaluator with default budget limits.

### Performance Context

**Why factorial(10)?**

- **Computationally Manageable**: 10! = 3,628,800 fits comfortably in standard integer types
- **Budget Safe**: Fits well within CEK machine limits for both recursive and iterative approaches
- **Optimization Sensitive**: Large enough to show compiler differences in loop vs recursion handling
- **Practical Scale**: Representative of many real-world mathematical computations

**Expected Performance Ranges** (approximate):

- **Recursive**: Higher CPU usage due to function call overhead
- **Iterative**: Lower CPU usage, more predictable memory patterns
- **Tail Recursive**: Moderate CPU usage with optimized stack handling

---

## Submission Checklist

Before submitting your implementation:

- [ ] **Verify Result**: Program produces exactly `3628800` when executed
- [ ] **Test Budget**: Execution completes without budget exhaustion
- [ ] **Prepare Files**:
- [ ] `factorial.uplc` - Your compiled UPLC program
- [ ] `metadata.json` - Compiler info, optimization settings, implementation notes
- [ ] `metrics.json` - Performance measurements (CPU, memory, script size, term size)
- [ ] `README.md` - Brief description of your approach
- [ ] **Directory Structure**: Place in `submissions/factorial/{Compiler}_{Version}_{Handle}/`
- [ ] **Schema Validation**: Ensure JSON files match required schemas

### File Templates

Use these templates from `submissions/TEMPLATE/`:

- `metadata-template.json` for compiler and build information
- `metrics-template.json` for performance measurements

---

## Local Validation

1. **Functional Test**: Execute your UPLC program and verify output is `3628800`
2. **Budget Test**: Ensure execution completes within CEK machine limits
3. **Consistency Test**: Run multiple times to confirm deterministic behavior
4. **Schema Test**: Validate JSON files against schemas in `submissions/TEMPLATE/`

### Example Validation Commands

```bash
# Measure your UPLC program (if using the cape tool)
cape submission measure factorial.uplc

# Validate submission files
cape submission validate submissions/factorial/YourCompiler_1.0.0_YourHandle/
```

---

## Technical Constraints

- **Plutus Core Version**: Target Plutus Core 1.1.0
- **Plutus Version**: V3 recommended (V1, V2 acceptable)
- **Budget Limits**: Must complete within standard CEK machine execution limits
- **No External Dependencies**: Program must be self-contained
- **Deterministic**: Must produce consistent results

---

## Verification Points

### Correctness Verification

1. **Base Case**: `factorial(0) = 1`
2. **Small Values**: `factorial(1) = 1`, `factorial(2) = 2`, `factorial(3) = 6`, `factorial(4) = 24`
3. **Target Value**: `factorial(10) = 3628800`

### Performance Verification

1. **CPU Budget Compliance**: Must execute within CEK machine limits
2. **Memory Budget Compliance**: Must not exceed memory allocation limits
3. **Script Size**: Should be reasonably compact for the computation performed
4. **Execution Consistency**: Should produce identical results across multiple runs

---

_This benchmark serves as both a correctness test and performance comparison tool, enabling compiler authors to validate their mathematical computation handling while providing standardized metrics for community comparison._
32 changes: 32 additions & 0 deletions submissions/factorial/Plinth_1.52.0.0_Unisay/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Benchmark Implementation Notes

**Scenario**: `factorial`

**Submission ID**: `Plinth_1.52.0.0_Unisay` (Format: `Language_Version_GitHubHandle`)

## Implementation Details

- **Compiler**: `Plinth 1.52.0.0`
- **Implementation Approach**: `recursive`
- **Compilation Flags**: Standard Plinth (PlutusTx) optimization flags

## Performance Results

- See [metrics.json](metrics.json) for detailed performance measurements
- **Result**: `factorial(10) = 3628800` ✓
- **CPU Units**: 5,859,917
- **Memory Units**: 21,751
- **Script Size**: 37 bytes

## Reproducibility

- **Source Available**: `true`
- **Source Repository**: https://github.com/IntersectMBO/UPLC-CAPE
- **Compilation Config**: Targeting Plutus Core 1.1.0 with standard PlutusTx plugin configuration

## Notes

- Uses recursive factorial implementation: `factorial(n) = if n <= 0 then 1 else n * factorial(n-1)`
- Much more efficient than fibonacci due to linear recursion vs exponential
- Compiled with PlutusTx plugin targeting Plutus Core 1.1.0
- Source code available in the `plinth/src/Factorial.hs` module
6 changes: 6 additions & 0 deletions submissions/factorial/Plinth_1.52.0.0_Unisay/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"comment": "Optional: Include compilation parameters that affect UPLC output",
"optimization_flags": [],
"compiler_settings": {},
"build_environment": {}
}
26 changes: 26 additions & 0 deletions submissions/factorial/Plinth_1.52.0.0_Unisay/factorial.uplc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
(program
1.1.0
[
[
(lam s-0 [ s-0 s-0 ])
(lam
s-1
(lam
n-2
(case
[ [ (builtin lessThanEqualsInteger) n-2 ] (con integer 0) ]
[
[ (builtin multiplyInteger) n-2 ]
[
[ s-1 s-1 ]
[ [ (builtin subtractInteger) n-2 ] (con integer 1) ]
]
]
(con integer 1)
)
)
)
]
(con integer 10)
]
)
29 changes: 29 additions & 0 deletions submissions/factorial/Plinth_1.52.0.0_Unisay/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"compiler": {
"name": "Plinth",
"version": "1.52.0.0",
"commit_hash": "800c67d38d01177fd0a36d01aa23ff7fea7bd1ba"
},
"compilation_config": {
"optimization_level": "Plinth",
"target": "uplc",
"flags": ["Plinth"],
"environment": {
"dependencies": {
"plutus-tx": "1.52.0.0",
"plutus-core": "1.52.0.0",
"plutus-ledger-api": "1.52.0.0"
}
}
},
"contributor": {
"name": "Unisay",
"organization": "UPLC-CAPE Project"
},
"submission": {
"date": "2025-08-18T08:00:00Z",
"source_available": true,
"source_repository": "https://github.com/IntersectMBO/UPLC-CAPE",
"implementation_notes": "Factorial implementation using Plinth (PlutusTx) with parameter 10, targeting Plutus Core 1.1.0. Recursive approach computing factorial(10) = 3628800."
}
}
15 changes: 15 additions & 0 deletions submissions/factorial/Plinth_1.52.0.0_Unisay/metrics.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"execution_environment": {
"evaluator": "PlutusTx.Eval-1.52.0.0"
},
"measurements": {
"cpu_units": 5859917,
"memory_units": 21751,
"script_size_bytes": 37,
"term_size": 29
},
"notes": "<optional notes>",
"scenario": "factorial",
"timestamp": "2025-08-18T08:00:10Z",
"version": "1.0.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Optional: Place your source code files here
12 changes: 12 additions & 0 deletions submissions/factorial/Plinth_1.52.0.0_Unisay/source/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Source Code

The source code for this factorial implementation is located in the main repository:

**[📁 plinth/src/Factorial.hs](../../../../plinth/src/Factorial.hs)**

This Haskell module contains:

- `factorial :: Integer -> Integer` - The factorial function implementation
- `factorial10Code :: CompiledCode Integer` - The compiled UPLC program for factorial(10)

The implementation uses recursive approach: `factorial(n) = if n <= 0 then 1 else n * factorial(n-1)`
14 changes: 14 additions & 0 deletions submissions/fibonacci/Plinth_1.49.0.0_Unisay/source/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Source Code

The source code for this fibonacci implementation is located in the main repository:

**[📁 plinth/src/Fibonacci.hs](../../../../plinth/src/Fibonacci.hs)**

This Haskell module contains:

- `fibonacci :: Integer -> Integer` - The fibonacci function implementation
- `fibonacci25Code :: CompiledCode Integer` - The compiled UPLC program for fibonacci(25)

The implementation uses recursive approach: `fibonacci(n) = if n <= 1 then n else fibonacci(n-1) + fibonacci(n-2)`

**Note**: This submission was created with Plinth 1.49.0.0, while the current source uses 1.52.0.0. The core algorithm remains the same.
Loading