arithmetic-circuits-0.2.0: test/Test/Circuit/Arithmetic.hs
{-# LANGUAGE DeriveAnyClass, DeriveGeneric, TupleSections #-}
module Test.Circuit.Arithmetic where
import Circuit.Affine
import Circuit.Arithmetic
import Data.Curve.Weierstrass.BN254 (Fr)
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Pairing.BN254 (getRootOfUnity)
import Fresh
import Protolude
import QAP
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck
-------------------------------------------------------------------------------
-- Test values
-------------------------------------------------------------------------------
testEqualCircuit :: ArithCircuit Fr
testEqualCircuit = ArithCircuit [Equal (InputWire 0) (IntermediateWire 0) (OutputWire 0)]
testInputMap :: Fr -> Map Int Fr
testInputMap = Map.singleton 0
testSplitUnsplitCircuit :: Int -> ArithCircuit Fr
testSplitUnsplitCircuit nbits =
ArithCircuit
[ Split (InputWire 0) midWires,
Mul (ConstGate 1) (unsplit midWires) (OutputWire 0)
]
where
midWires = fmap IntermediateWire [0 .. nbits - 1]
-------------------------------------------------------------------------------
-- Generators
-------------------------------------------------------------------------------
arbVars :: Int -> [Int] -> [Gen (AffineCircuit Wire f)]
arbVars numInps mids =
varInps numInps ++ varMids mids
where
varInps size
| size <= 0 = []
| otherwise = [Var . InputWire <$> choose (0, numInps - 1)]
varMids [] = []
varMids ms@(_ : _) = [Var . IntermediateWire <$> elements ms]
arbAffineCircuitWithMids ::
Arbitrary f =>
Int ->
[Int] ->
Int ->
Gen (AffineCircuit Wire f)
arbAffineCircuitWithMids numInps mids size
| size <= 0 =
oneof $ [ConstGate <$> arbitrary] ++ arbVars numInps mids
| size > 0 =
oneof
[ ScalarMul <$> arbitrary <*> arbAffineCircuitWithMids numInps mids (size - 1),
Add <$> arbAffineCircuitWithMids numInps mids (size - 1)
<*> arbAffineCircuitWithMids numInps mids (size - 1)
]
arbInputVector :: (Arbitrary f) => Int -> Gen (Map Int f)
arbInputVector numVars = Map.fromList . zip [0 ..] <$> vector numVars
arbArithCircuit ::
Arbitrary f =>
-- | distribution of frequency of mul/equal/split
-- gates, respectively
(Int, Int, Int) ->
Int ->
Int ->
Gen (ArithCircuit f)
arbArithCircuit (distMul, distEqual, distSplit) numInps size
| size <= 0 =
pure $ ArithCircuit []
| size > 0 =
do
ArithCircuit gates <- arbArithCircuit (distMul, distEqual, distSplit) numInps (size - 1)
let mids = [i | IntermediateWire i <- concatMap outputWires gates]
frequency . catMaybes $
[ (distMul,) <$> mulGate gates mids,
(distEqual,) <$> equalGate gates mids,
(distSplit,) <$> splitGate gates mids
]
where
mulGate gates mids =
Just $ do
lhs <- arbAffineCircuitWithMids numInps mids 1
rhs <- arbAffineCircuitWithMids numInps mids 1
let outWire = case mids of
[] -> 0
ms@(_ : _) -> maximum ms + 1
gate = Mul lhs rhs (IntermediateWire outWire)
pure . ArithCircuit $ gates ++ [gate]
equalGate _ [] =
Nothing
equalGate gates mids@(_ : _) =
Just $ do
inp <- elements mids
let outWire =
case mids of
[] -> 0
ms@(_ : _) -> maximum ms + 1
gate =
Equal
(IntermediateWire inp)
(IntermediateWire outWire)
(IntermediateWire $ outWire + 1)
pure . ArithCircuit $ gates ++ [gate]
splitGate _ [] =
Nothing
splitGate gates mids@(_ : _) =
Just $ do
inp <- IntermediateWire <$> elements mids
let firstOutWire =
case mids of
[] -> 0
ms@(_ : _) -> maximum ms + 1
nbits = 256
outWires = fmap IntermediateWire [firstOutWire .. firstOutWire + nbits - 1]
gate = Split inp outWires
pure . ArithCircuit $ gates ++ [gate]
-- | The input vector has to have the correct length, so we want to
-- generate the program and the test input simultaneously.
data ArithCircuitWithInputs f = ArithCircuitWithInputs (ArithCircuit f) [Map Int f]
deriving (Show, Generic, NFData)
instance (Arbitrary f, Num f) => Arbitrary (ArithCircuitWithInputs f) where
arbitrary = do
numVars <- abs <$> arbitrary `suchThat` (> 0)
program <- sized (arbArithCircuit (50, 10, 1) numVars)
inputs <- vectorOf 5 $ arbInputVector numVars
pure $ ArithCircuitWithInputs program inputs
data ArithCircuitWithInput f = ArithCircuitWithInput (ArithCircuit f) (Map Int f)
deriving (Show, Generic, NFData)
instance (Arbitrary f, Num f) => Arbitrary (ArithCircuitWithInput f) where
arbitrary = do
numVars <- abs <$> arbitrary `suchThat` (> 0)
program <- sized (arbArithCircuit (50, 10, 1) numVars)
input <- arbInputVector numVars
pure $ ArithCircuitWithInput program input
-------------------------------------------------------------------------------
-- Tests
-------------------------------------------------------------------------------
unit_eqGate ::
Assertion
unit_eqGate =
do
testEvalWith 0 @?= Just 0
testEvalWith 1 @?= Just 1
testEvalWith 2 @?= Just 1
testEvalWith 3 @?= Just 1
where
testEvalWith n =
lookupAtWire (OutputWire 0) $
evalArithCircuit
lookupAtWire
updateAtWire
testEqualCircuit
(initialQapSet $ testInputMap n)
unit_splitUnsplit :: Assertion
unit_splitUnsplit =
mapM_ (\n -> testSplitUnsplit n @?= Just n) (fmap fromIntegral [0 .. 2 ^ nbits - 1])
where
nbits = 16
testSplitUnsplit n =
lookupAtWire (OutputWire 0) $
evalArithCircuit
lookupAtWire
updateAtWire
(testSplitUnsplitCircuit nbits)
(initialQapSet $ testInputMap n)
prop_arithCircuitValid :: ArithCircuitWithInputs Fr -> Bool
prop_arithCircuitValid (ArithCircuitWithInputs program _) =
validArithCircuit program
prop_arithCircuitToQAP_slow :: ArithCircuitWithInputs Fr -> Property
prop_arithCircuitToQAP_slow (ArithCircuitWithInputs program inputs) =
withMaxSuccess 10 $
case program of
ArithCircuit [] -> True
ArithCircuit (_ : _) -> all testInput inputs
where
roots = evalFresh $ generateRoots (fromIntegral . (+ 1) <$> fresh) program
qap = arithCircuitToQAP roots program
testInput input =
verifyAssignment qap $ generateAssignment program input
prop_arithCircuitToQAP_fft :: ArithCircuitWithInputs Fr -> Property
prop_arithCircuitToQAP_fft (ArithCircuitWithInputs program inputs) =
withMaxSuccess 10 $
case program of
ArithCircuit [] -> True
ArithCircuit (_ : _) -> all testInput inputs
where
roots = evalFresh $ generateRoots (fromIntegral . (+ 1) <$> fresh) program
qap = createPolynomialsFFT getRootOfUnity $ arithCircuitToGenQAP roots program
testInput input = verifyAssignment qap $ generateAssignment program input