packages feed

arithmetic-circuits-0.2.0: test/Test/Circuit/Affine.hs

module Test.Circuit.Affine where

import           Circuit.Affine
import qualified Data.Map              as Map
import           Protolude
import           Test.Tasty.QuickCheck

-------------------------------------------------------------------------------
-- Generators
-------------------------------------------------------------------------------

arbAffineCircuit ::
  Arbitrary f =>
  Int ->
  Int ->
  Gen (AffineCircuit Int f)
arbAffineCircuit numVars size
  | size <= 0 =
    oneof $
      [ ConstGate <$> arbitrary
      ]
        ++ if numVars > 0
          then [Var <$> choose (0, numVars - 1)]
          else []
  | size > 0 =
    oneof
      [ ScalarMul <$> arbitrary <*> arbAffineCircuit numVars (size - 1),
        Add <$> arbAffineCircuit numVars (size - 1)
          <*> arbAffineCircuit numVars (size - 1)
      ]

arbInputVector :: Arbitrary f => Int -> Gen (Map Int f)
arbInputVector numVars = Map.fromList . zip [0 ..] <$> vector numVars

-- | The input vector has to have the correct length, so we want to
-- generate the program and the test input simultaneously.
data AffineCircuitWithInputs f = AffineCircuitWithInputs (AffineCircuit Int f) [Map Int f]
  deriving (Show)

instance Arbitrary f => Arbitrary (AffineCircuitWithInputs f) where
  arbitrary = do
    numVars <- abs <$> arbitrary
    program <- scale (`div` 7) $ sized (arbAffineCircuit numVars)
    inputs <- vectorOf 10 $ arbInputVector numVars
    pure $ AffineCircuitWithInputs program inputs

-------------------------------------------------------------------------------
-- Tests
-------------------------------------------------------------------------------

-- | Check that evaluating the vector representation of the circuit
-- yields the same results as evaluating the circuit "directly". Field
-- is instantiated as being the rationals for testing. It later should
-- probably be something like Pairing.Fr.Fr.
prop_affineCircuitToAffineMap ::
  AffineCircuitWithInputs Rational -> Bool
prop_affineCircuitToAffineMap (AffineCircuitWithInputs program inputs) =
  all testInput inputs
  where
    testInput input =
      evalAffineCircuit Map.lookup input program
        == evalAffineMap (affineCircuitToAffineMap program) input