Skip to content

Commit

Permalink
Merge pull request #214 from JohnLCaron/contestStatus
Browse files Browse the repository at this point in the history
New way to set contest status from its assertions' status.
  • Loading branch information
JohnLCaron authored Feb 21, 2025
2 parents dbd9c40 + 1fe2bff commit 38dd5f7
Show file tree
Hide file tree
Showing 43 changed files with 123 additions and 125 deletions.
25 changes: 13 additions & 12 deletions core/src/main/kotlin/org/cryptobiotic/rlauxe/core/RiskTestingFn.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
package org.cryptobiotic.rlauxe.core

enum class TestH0Status(val complete: Boolean, val success: Boolean) {
InProgress(false, false),
// NOTE: leave them in this order, as contest status is min rank
enum class TestH0Status(val rank: Int, val complete: Boolean, val success: Boolean) {
InProgress(0,false, false),

// contest status
ContestMisformed(1,true, false), // Contest incorrectly formed
MinMargin(2,true, false), // margin too small for RLA to efficiently work
TooManyPhantoms(3,true, false), // too many phantoms, makes margin < 0
FailMaxSamplesAllowed(4,true, false), // estimated samples greater than maximum samples allowed

// possible returns from RiskTestingFn
StatRejectNull(true, true), // statistical rejection of H0
LimitReached(false, false), // cant tell from the number of samples available
StatRejectNull(10,true, true), // statistical rejection of H0
LimitReached(11,false, false), // cant tell from the number of samples available
//// only when sampling without replacement all the way close to Nc
SampleSumRejectNull(true, false), // SampleSum > Nc / 2, so we know H0 is false
AcceptNull(true, false), // SampleSum + (all remaining ballots == 1) < Nc / 2, so we know that H0 is true.

// contest status
ContestMisformed(true, false), // Contest incorrectly formed
MinMargin(true, false), // margin too small for RLA to efficiently work
TooManyPhantoms(true, false), // too many phantoms, makes margin < 0
FailMaxSamplesAllowed(true, false), // estimated samples greater than maximum samples allowed
SampleSumRejectNull(12,true, false), // SampleSum > Nc / 2, so we know H0 is false
AcceptNull(13,true, false), // SampleSum + (all remaining ballots == 1) < Nc / 2, so we know that H0 is true.
}

data class TestH0Result(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class SimulateSampleSizeTask(
override fun name() = "task ${contestUA.name} ${assertion.assorter.desc()}}"
override fun run(): EstimationResult {
val result: RunTestRepeatedResult = when (auditConfig.auditType) {
AuditType.CARD_COMPARISON ->
AuditType.CLCA ->
simulateSampleSizeClcaAssorter(
auditConfig,
contestUA.contest,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.cryptobiotic.rlauxe.workflow
import org.cryptobiotic.rlauxe.core.ClcaErrorRates
import org.cryptobiotic.rlauxe.util.secureRandom

enum class AuditType { POLLING, CARD_COMPARISON, ONEAUDIT }
enum class AuditType { POLLING, CLCA, ONEAUDIT }

data class AuditConfig(
val auditType: AuditType,
Expand All @@ -28,7 +28,7 @@ data class AuditConfig(
appendLine(" nsimEst=$nsimEst, quantile=$quantile, samplePctCutoff=$samplePctCutoff, minMargin=$minMargin version=$version")
when (auditType) {
AuditType.POLLING -> appendLine(" $pollingConfig")
AuditType.CARD_COMPARISON -> appendLine(" $clcaConfig")
AuditType.CLCA -> appendLine(" $clcaConfig")
AuditType.ONEAUDIT -> appendLine(" $oaConfig, ")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class ClcaWorkflow(
val contestsUA: List<ContestUnderAudit>
val cvrsUA: List<CvrUnderAudit>
init {
require (auditConfig.auditType == AuditType.CARD_COMPARISON)
require (auditConfig.auditType == AuditType.CLCA)

// 2. Pre-processing and consistency checks
// a) Check that the winners according to the CVRs are the reported winners.
Expand Down Expand Up @@ -78,7 +78,7 @@ class ClcaWorkflow(

/////////////////////////////////////////////////////////////////////////////////

// The auditors retrieved the indicated cards, manually read the votes from those cards, and input the MVRs
// TODO lot of common code between the audit types...
fun runClcaAudit(auditConfig: AuditConfig,
contestsUA: List<ContestUnderAudit>,
sampleIndices: List<Int>,
Expand All @@ -104,25 +104,21 @@ fun runClcaAudit(auditConfig: AuditConfig,
val cvrPairs: List<Pair<Cvr, Cvr>> = mvrs.zip(sampledCvrs)
cvrPairs.forEach { (mvr, cvr) -> require(mvr.id == cvr.id) }

// TODO could parallelize across assertions
if (!quiet) println("runAudit round $roundIdx")
var allDone = true
contestsNotDone.forEach { contestUA ->
var allAssertionsDone = true
var contestAssertionStatus = mutableListOf<TestH0Status>()
contestUA.clcaAssertions.forEach { cassertion ->
if (!cassertion.status.complete) {
val testH0Result = auditClcaAssertion(auditConfig, contestUA, cassertion, cvrPairs, roundIdx, quiet=quiet)
cassertion.status = testH0Result.status
cassertion.round = roundIdx
allAssertionsDone = allAssertionsDone && cassertion.status.complete
}
contestAssertionStatus.add(cassertion.status)
}
if (allAssertionsDone) {
contestUA.done = true
contestUA.status = TestH0Status.StatRejectNull // TODO ???
}
contestUA.done = contestAssertionStatus.all { it.complete }
contestUA.status = contestAssertionStatus.minBy { it.rank } // use lowest rank status.
allDone = allDone && contestUA.done

}
return allDone
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,43 +46,49 @@ class OneAuditWorkflow(
return sample(this, roundIdx, quiet)
}

// The auditors retrieve the indicated cards, manually read the votes from those cards, and input the MVRs
override fun runAudit(sampleIndices: List<Int>, mvrs: List<Cvr>, roundIdx: Int): Boolean {
val contestsNotDone = contestsUA.filter{ !it.done }
val sampledCvrs = sampleIndices.map { cvrs[it] }

// prove that sampledCvrs correspond to mvrs
require(sampledCvrs.size == mvrs.size)
val cvrPairs: List<Pair<Cvr, Cvr>> = mvrs.zip(sampledCvrs)
cvrPairs.forEach { (mvr, cvr) -> require(mvr.id == cvr.id) }

if (!quiet) println("runAudit round $roundIdx")
var allDone = true
contestsNotDone.forEach { contestUA ->
var allAssertionsDone = true
contestUA.clcaAssertions.forEach { assertion ->
if (!assertion.status.complete) {
val testH0Result = runOneAuditAssertionAlpha(auditConfig, contestUA, assertion, cvrPairs, roundIdx, quiet=quiet)
assertion.status = testH0Result.status
assertion.round = roundIdx
allAssertionsDone = allAssertionsDone && assertion.status.complete
}
}
if (allAssertionsDone) {
contestUA.done = true
contestUA.status = TestH0Status.StatRejectNull // TODO
}
allDone = allDone && contestUA.done

}
return allDone
return runOneAudit(auditConfig, contestsUA, sampleIndices, mvrs, cvrs, roundIdx, quiet)
}

override fun auditConfig() = this.auditConfig
override fun getContests(): List<ContestUnderAudit> = contestsUA
override fun getBallotsOrCvrs() : List<BallotOrCvr> = cvrsUA
}

fun runOneAudit(auditConfig: AuditConfig,
contestsUA: List<ContestUnderAudit>,
sampleIndices: List<Int>,
mvrs: List<Cvr>,
cvrs: List<Cvr>,
roundIdx: Int,
quiet: Boolean): Boolean {
val contestsNotDone = contestsUA.filter{ !it.done }
val sampledCvrs = sampleIndices.map { cvrs[it] }

// prove that sampledCvrs correspond to mvrs
require(sampledCvrs.size == mvrs.size)
val cvrPairs: List<Pair<Cvr, Cvr>> = mvrs.zip(sampledCvrs)
cvrPairs.forEach { (mvr, cvr) -> require(mvr.id == cvr.id) }

if (!quiet) println("runAudit round $roundIdx")
var allDone = true
contestsNotDone.forEach { contestUA ->
var contestAssertionStatus = mutableListOf<TestH0Status>()
contestUA.clcaAssertions.forEach { cassertion ->
if (!cassertion.status.complete) {
val testH0Result = runOneAuditAssertionAlpha(auditConfig, contestUA, cassertion, cvrPairs, roundIdx, quiet=quiet)
cassertion.status = testH0Result.status
cassertion.round = roundIdx
}
contestAssertionStatus.add(cassertion.status)
}
contestUA.done = contestAssertionStatus.all { it.complete }
contestUA.status = contestAssertionStatus.minBy { it.rank } // use lowest rank status.
allDone = allDone && contestUA.done
}
return allDone
}

fun runOneAuditAssertionAlpha(
auditConfig: AuditConfig,
contestUA: ContestUnderAudit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import org.cryptobiotic.rlauxe.core.CvrUnderAudit
import org.cryptobiotic.rlauxe.estimate.*
import kotlin.math.max

/** created from persistent state */
/** Created from persistent state. See rla/src/main/kotlin/org/cryptobiotic/rlauxe/cli/RunRlaStartTest.kt */
class PersistentWorkflow(
val auditConfig: AuditConfig,
val contestsUA: List<ContestUnderAudit>,
Expand All @@ -32,10 +32,10 @@ class PersistentWorkflow(
}

override fun runAudit(sampleIndices: List<Int>, mvrs: List<Cvr>, roundIdx: Int): Boolean {
return if (auditConfig.auditType == AuditType.POLLING) {
runPollingAudit(auditConfig, contestsUA, mvrs, roundIdx, quiet)
} else {
runClcaAudit(auditConfig, contestsUA, sampleIndices, mvrs, cvrs, roundIdx, quiet)
return when (auditConfig.auditType) {
AuditType.CLCA -> runClcaAudit(auditConfig, contestsUA, sampleIndices, mvrs, cvrs, roundIdx, quiet)
AuditType.POLLING -> runPollingAudit(auditConfig, contestsUA, mvrs, roundIdx, quiet)
AuditType.ONEAUDIT -> runOneAudit(auditConfig, contestsUA, sampleIndices, mvrs, cvrs, roundIdx, quiet)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,20 @@ fun runPollingAudit(
return true
}

if (!quiet) println("runAudit round $roundIdx")
var allDone = true
contestsNotDone.forEach { contestUA ->
var allAssertionsDone = true
var contestAssertionStatus = mutableListOf<TestH0Status>()
contestUA.pollingAssertions.forEach { assertion ->
if (!assertion.status.complete) {
val testResult = auditPollingAssertion(auditConfig, contestUA.contest as Contest, assertion, mvrs, roundIdx, quiet)
assertion.status = testResult.status
val testH0Result = auditPollingAssertion(auditConfig, contestUA.contest as Contest, assertion, mvrs, roundIdx, quiet)
assertion.status = testH0Result.status
assertion.round = roundIdx
allAssertionsDone = allAssertionsDone && assertion.status.complete
}
contestAssertionStatus.add(assertion.status)
}
if (allAssertionsDone) {
contestUA.done = true
contestUA.status = TestH0Status.StatRejectNull // TODO
}
contestUA.done = contestAssertionStatus.all { it.complete }
contestUA.status = contestAssertionStatus.minBy { it.rank } // use lowest rank status.
allDone = allDone && contestUA.done
}
return allDone
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class MeasureEstimationTaskConcurrency {
val nassertions = contestsUA.sumOf { it.assertions().size }
println("ncontests=${contestsUA.size} nassertions=${nassertions} ncvrs=${cvrs.size}")

val auditConfig = AuditConfig(AuditType.CARD_COMPARISON, hasStyles = true, nsimEst = 10)
val auditConfig = AuditConfig(AuditType.CLCA, hasStyles = true, nsimEst = 10)
val tasks = mutableListOf<ConcurrentTaskG<EstimationResult>>()

contestsUA.filter { !it.done }.forEach { contestUA ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import org.cryptobiotic.rlauxe.estimate.makeFuzzedCvrsFrom
import kotlin.test.Test

class TestClcaWorkflow {
val auditConfig = AuditConfig(AuditType.CARD_COMPARISON, hasStyles=true, nsimEst=10)
val auditConfig = AuditConfig(AuditType.CLCA, hasStyles=true, nsimEst=10)

@Test
fun testComparisonOneContest() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class TestClcaWorkflowNoStyles {
val testData = MultiContestTestData(ncontests, nbs, N, marginRange =marginRange, underVotePctRange =underVotePct, phantomPctRange =phantomRange)

val errorRates = ClcaErrorRates(0.0, phantomPct, 0.0, 0.0, )
val auditConfig = AuditConfig(AuditType.CARD_COMPARISON, hasStyles=false, seed=12356667890L, nsimEst=10,
val auditConfig = AuditConfig(AuditType.CLCA, hasStyles=false, seed=12356667890L, nsimEst=10,
clcaConfig = ClcaConfig(ClcaStrategyType.apriori, errorRates=errorRates))

testComparisonWorkflow(auditConfig, testData)
Expand All @@ -37,7 +37,7 @@ class TestClcaWorkflowNoStyles {
val testData = MultiContestTestData(ncontests, nbs, N, marginRange =marginRange, underVotePctRange =underVotePct, phantomPctRange =phantomRange)

val errorRates = ClcaErrorRates(0.0, phantomPct, 0.0, 0.0, )
val auditConfig = AuditConfig(AuditType.CARD_COMPARISON, hasStyles=false, seed=12356667890L, nsimEst=10,
val auditConfig = AuditConfig(AuditType.CLCA, hasStyles=false, seed=12356667890L, nsimEst=10,
clcaConfig = ClcaConfig(ClcaStrategyType.apriori, errorRates=errorRates))

testComparisonWorkflow(auditConfig, testData)
Expand All @@ -52,7 +52,7 @@ class TestClcaWorkflowNoStyles {

@Test
fun noErrorsNoPhantoms() {
val auditConfig = AuditConfig(AuditType.CARD_COMPARISON, hasStyles=false, nsimEst=10)
val auditConfig = AuditConfig(AuditType.CLCA, hasStyles=false, nsimEst=10)
val N = 100000
val ncontests = 11
val nbs = 4
Expand All @@ -65,7 +65,7 @@ class TestClcaWorkflowNoStyles {

@Test
fun noErrorsWithPhantoms() {
val auditConfig = AuditConfig(AuditType.CARD_COMPARISON, hasStyles=false, nsimEst=10)
val auditConfig = AuditConfig(AuditType.CLCA, hasStyles=false, nsimEst=10)
val N = 100000
val ncontests = 42
val nbs = 11
Expand All @@ -88,14 +88,14 @@ class TestClcaWorkflowNoStyles {
val testData = MultiContestTestData(ncontests, nbs, N, marginRange =marginRange, underVotePctRange =underVotePct, phantomPctRange =phantomRange)

val errorRates = ClcaErrorRates(0.0, phantomPct, 0.0, 0.0, )
val auditConfig = AuditConfig(AuditType.CARD_COMPARISON, hasStyles=true, nsimEst=10,
val auditConfig = AuditConfig(AuditType.CLCA, hasStyles=true, nsimEst=10,
clcaConfig = ClcaConfig(ClcaStrategyType.apriori, errorRates=errorRates))
testComparisonWorkflow(auditConfig, testData)
}

@Test
fun testComparisonWithFuzz() {
val auditConfig = AuditConfig(AuditType.CARD_COMPARISON, hasStyles=true, nsimEst=10,
val auditConfig = AuditConfig(AuditType.CLCA, hasStyles=true, nsimEst=10,
clcaConfig = ClcaConfig(ClcaStrategyType.fuzzPct, simFuzzPct = 0.01))

val N = 50000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import org.cryptobiotic.rlauxe.estimate.makeFuzzedCvrsFrom
import kotlin.test.Test

class TestOneRoundClcaWorkflow {
val auditConfig = AuditConfig(AuditType.CARD_COMPARISON, hasStyles=true, nsimEst=10)
val auditConfig = AuditConfig(AuditType.CLCA, hasStyles=true, nsimEst=10)

@Test
fun testOneContest() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ class TestRaireWorkflow {

@Test
fun testRaireComparisonWithStyle() {
testRaireWorkflow(AuditConfig(AuditType.CARD_COMPARISON, hasStyles=true, nsimEst=10))
testRaireWorkflow(AuditConfig(AuditType.CLCA, hasStyles=true, nsimEst=10))
}

@Test
fun testRaireComparisonNoStyle() {
testRaireWorkflow(AuditConfig(AuditType.CARD_COMPARISON, hasStyles=false, nsimEst=10))
testRaireWorkflow(AuditConfig(AuditType.CLCA, hasStyles=false, nsimEst=10))
}

fun testRaireWorkflow(auditConfig: AuditConfig) {
Expand All @@ -26,7 +26,7 @@ class TestRaireWorkflow {
@Test
fun testRaireFuzz() {
val mvrFuzzPct = .02
val auditConfig = AuditConfig(AuditType.CARD_COMPARISON, hasStyles=false, nsimEst=10,
val auditConfig = AuditConfig(AuditType.CLCA, hasStyles=false, nsimEst=10,
clcaConfig = ClcaConfig(ClcaStrategyType.fuzzPct, simFuzzPct = mvrFuzzPct))

val (rcontest: RaireContestUnderAudit, cvrs: List<Cvr>) = makeRaireContest(N=20000, minMargin=.04, quiet = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ClcaOneRoundAuditTaskGenerator(

override fun generateNewTask(): OneRoundAuditTask {
val useConfig = auditConfig ?:
AuditConfig(AuditType.CARD_COMPARISON, true, nsimEst = nsimEst, samplePctCutoff=1.0,
AuditConfig(AuditType.CLCA, true, nsimEst = nsimEst, samplePctCutoff=1.0,
clcaConfig = clcaConfigIn ?: ClcaConfig(ClcaStrategyType.noerror))

val sim = ContestSimulation.make2wayTestContest(Nc=Nc, margin, undervotePct=underVotePct, phantomPct=phantomPct)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class ClcaWorkflowTaskGenerator(

override fun generateNewTask(): WorkflowTask {
val useConfig = auditConfig ?:
AuditConfig(AuditType.CARD_COMPARISON, true, nsimEst = nsimEst, samplePctCutoff=1.0,
AuditConfig(AuditType.CLCA, true, nsimEst = nsimEst, samplePctCutoff=1.0,
clcaConfig = clcaConfigIn ?: ClcaConfig(ClcaStrategyType.noerror))

val sim = ContestSimulation.make2wayTestContest(Nc=Nc, margin, undervotePct=underVotePct, phantomPct=phantomPct)
Expand Down Expand Up @@ -209,7 +209,7 @@ class RaireWorkflowTaskGenerator(

override fun generateNewTask(): WorkflowTask {
val useConfig = auditConfig ?:
AuditConfig(AuditType.CARD_COMPARISON, true, nsimEst = nsimEst, samplePctCutoff=1.0,
AuditConfig(AuditType.CLCA, true, nsimEst = nsimEst, samplePctCutoff=1.0,
clcaConfig = clcaConfigIn ?: ClcaConfig(ClcaStrategyType.noerror))

val (rcontest, testCvrs) = makeRaireContest(N=Nc, minMargin=margin, undervotePct=underVotePct, phantomPct=phantomPct, quiet = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class GenerateClcaErrorTable {

@Test
fun generateErrorTable() {
val auditConfig = AuditConfig(AuditType.CARD_COMPARISON, hasStyles = true, seed = 12356667890L, nsimEst = 1000)
val auditConfig = AuditConfig(AuditType.CLCA, hasStyles = true, seed = 12356667890L, nsimEst = 1000)
val N = 100000

// TODO how much do the rates depend on the margin?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class CorlaWorkflowTaskGenerator(
override fun generateNewTask(): WorkflowTask {
val auditConfig = auditConfigIn ?:
AuditConfig(
AuditType.CARD_COMPARISON, true, nsimEst = 10,
AuditType.CLCA, true, nsimEst = 10,
clcaConfig = clcaConfigIn ?: ClcaConfig(ClcaStrategyType.fuzzPct, mvrsFuzzPct)
)

Expand Down Expand Up @@ -54,7 +54,7 @@ class CorlaWorkflow(
val cvrsUA: List<CvrUnderAudit>

init {
require (auditConfig.auditType == AuditType.CARD_COMPARISON)
require (auditConfig.auditType == AuditType.CLCA)

// 2. Pre-processing and consistency checks
// a) Check that the winners according to the CVRs are the reported winners.
Expand Down
Loading

0 comments on commit 38dd5f7

Please sign in to comment.