ywcb00 commented on code in PR #2539:
URL: https://github.com/apache/systemds/pull/2539#discussion_r3656250757
##########
src/main/java/org/apache/sysds/common/Builtins.java:
##########
@@ -116,6 +116,9 @@ public enum Builtins {
DECISIONTREEPREDICT("decisionTreePredict", true),
DECOMPRESS("decompress", false),
DEDUP("dedup", true),
+ DP_LAPLACE("dp_laplace", false),
+ DP_GAUSSIAN("dp_gaussian", false),
+ DP_SET_BUDGET("dp_set_budget", false),
Review Comment:
Keep the alphabetical order.
##########
src/main/java/org/apache/sysds/common/InstructionType.java:
##########
@@ -63,6 +63,7 @@ public enum InstructionType {
MMChain,
Union,
EINSUM,
+ DPBuiltin,
Review Comment:
Do we need a separate instruction type or could we also use the already
existing `ParameterizedBuiltin` type?
##########
src/main/java/org/apache/sysds/runtime/privacy/dp/DPBudgetAccountant.java:
##########
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.runtime.privacy.dp;
+
+import org.apache.sysds.runtime.DMLRuntimeException;
+import org.apache.sysds.runtime.instructions.cp.DPBuiltinCPInstruction;
+
+/**
+ * Session-scoped differential privacy budget accountant.
+ *
+ * Tracks composition of DP releases across the lifetime of a DML script
execution. Each call to {@link #compose}
+ * records one release and checks whether the cumulative privacy cost has
exceeded the user-specified budget.
+ *
+ * The mechanism type (Laplace vs Gaussian) is inferred from the {@code delta}
argument passed to {@link #compose}:
+ *
+ * - Laplace (delta == 0): pure ε-DP. The budget cost is tracked via basic
composition — each release contributes
+ * exactly its ε to a running sum. This is the tightest possible bound for
pure DP and avoids the looser estimate that
+ * results from routing Laplace through the RDP conversion path (which would
introduce an unnecessary δ). Noise scale is
+ * calibrated to L1 sensitivity (see {@link #compose}). - Gaussian (delta >
0): (ε, δ)-DP via Rényi DP composition.
Review Comment:
Please do not include extra special symbols like 'ε', 'δ', '—', '→', etc. in
the code files. Replace these symbols by 'epsilon', 'delta', '-'.
##########
src/test/java/org/apache/sysds/test/component/cp/DPBuiltinCPInstructionTest.java:
##########
@@ -0,0 +1,409 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.test.component.cp;
+
+import org.apache.sysds.parser.DMLProgram;
+import org.apache.sysds.runtime.DMLRuntimeException;
+import org.apache.sysds.runtime.controlprogram.Program;
+import org.apache.sysds.runtime.controlprogram.context.ExecutionContext;
+import org.apache.sysds.runtime.controlprogram.context.ExecutionContextFactory;
+import org.apache.sysds.runtime.privacy.dp.DPBudgetAccountant;
+
+import org.junit.Test;
+import org.junit.Assert;
+
+/**
+ * Tests for {@code DPBuiltinCPInstruction} and {@code DPBudgetAccountant}.
+ *
+ * The tests are grouped into three levels: - Unit tests on DPBudgetAccountant
— verify composition, conversion, and
+ * budget enforcement in isolation, with no dependency on the full SystemDS
runtime. - Noise distribution tests — verify
+ * that the noise blocks generated by the Laplace and Gaussian mechanisms have
statistically correct means and variances
+ * (Kolmogorov-Smirnov style sanity checks). - DML integration tests</b> — run
complete DML scripts and verify
+ * end-to-end correctness via the existing AutomatedTestBase machinery.
+ *
+ * The DML integration tests require a built SystemDS jar and are separated
into a companion class
+ * {@link org.apache.sysds.test.functions.privacy.dp.DPBuiltinDMLTest}.
+ */
+public class DPBuiltinCPInstructionTest {
+
+ private static final double EPS = 1e-9;
+
+ //
=======================================================================
+ // 1. DPBudgetAccountant unit tests
+ //
=======================================================================
+
+ @Test
+ public void testAccountantInitialisesAtZeroCost() {
+ DPBudgetAccountant acc = new DPBudgetAccountant(1.0, 1e-5);
+ // No releases yet: total cost should be a large negative number
+ // (conversion formula gives -∞ when rdpSum = 0 for all orders),
+ // so remainingBudget() should exceed the budget.
+ Assert.assertTrue("No releases should leave budget intact",
acc.remainingBudget() > 0);
+ Assert.assertEquals(0, acc.releaseCount());
+ }
+
+ @Test
+ public void testSingleLaplaceReleaseDoesNotExceedBudget() {
+ // epsilon=0.5, budget=1.0: one release should consume < budget.
+ DPBudgetAccountant acc = new DPBudgetAccountant(1.0, 1e-5);
+ acc.compose(0.5, 0.0, 1.0); // Laplace, sensitivity=1
+ Assert.assertEquals(1, acc.releaseCount());
+ Assert.assertTrue("Single release within budget",
acc.totalEpsilonSpent() <= 1.0);
+ }
+
+ @Test
+ public void testSingleGaussianReleaseDoesNotExceedBudget() {
+ DPBudgetAccountant acc = new DPBudgetAccountant(1.0, 1e-5);
+ acc.compose(0.5, 1e-5, 1.0); // Gaussian
+ Assert.assertEquals(1, acc.releaseCount());
+ Assert.assertTrue("Single Gaussian release within budget",
acc.totalEpsilonSpent() <= 1.0);
+ }
+
+ @Test(expected = DMLRuntimeException.class)
+ public void testBudgetExhaustionThrows() {
+ // Budget = 0.1, but we try to make 10 releases at epsilon=0.5
each.
+ // After enough releases the budget must be exceeded.
+ DPBudgetAccountant acc = new DPBudgetAccountant(0.1, 1e-5);
+ for(int i = 0; i < 10; i++) {
+ acc.compose(0.5, 0.0, 1.0); // will throw before the
10th
+ }
+ }
+
+ @Test
+ public void testCompositionIsMonotonicallyIncreasing() {
+ DPBudgetAccountant acc = new DPBudgetAccountant(100.0, 1e-5);
// large budget
+ double prev = acc.totalEpsilonSpent();
+ for(int i = 0; i < 5; i++) {
+ acc.compose(0.3, 1e-5, 1.0);
+ double current = acc.totalEpsilonSpent();
+ Assert.assertTrue("Epsilon spent must increase with
each release", current > prev);
+ prev = current;
+ }
+ }
+
+ @Test
+ public void testGaussianTighterThanLaplaceForSameEpsilon() {
+ // For the same nominal (ε, δ), Gaussian uses RDP composition
which
+ // is tighter than Laplace with basic composition. After 5
releases:
+ // Laplace (basic, worst-case): 5ε
+ // Gaussian (RDP) : something < 5ε
+ double eps = 0.5;
+ double delta = 1e-5;
+
+ DPBudgetAccountant gaussian = new DPBudgetAccountant(100.0,
delta);
+ DPBudgetAccountant laplace = new DPBudgetAccountant(100.0,
delta);
+
+ for(int i = 0; i < 5; i++) {
+ gaussian.compose(eps, delta, 1.0);
+ laplace.compose(eps, 0.0, 1.0);
+ }
+
+ // After 5 releases, Gaussian RDP bound should be tighter.
+ // (Both may be < 5*eps; the point is Gaussian <= Laplace.)
+ Assert.assertTrue("Gaussian RDP bound should be <= Laplace
bound after 5 releases",
+ gaussian.totalEpsilonSpent() <=
laplace.totalEpsilonSpent() + 1e-6);
+ }
+
+ @Test
+ public void testRemainingBudgetDecreasesMonotonically() {
+ DPBudgetAccountant acc = new DPBudgetAccountant(2.0, 1e-5);
+ double prev = acc.remainingBudget();
+ for(int i = 0; i < 3; i++) {
+ acc.compose(0.2, 1e-5, 1.0);
+ double current = acc.remainingBudget();
+ Assert.assertTrue("Remaining budget must decrease",
current < prev);
+ prev = current;
+ }
+ }
+
+ @Test
+ public void testHigherEpsilonCostMoreForLaplace() {
+ // For Laplace, the accountant uses basic (pure ε-DP)
composition: cost = epsilon.
+ // Sensitivity determines noise scale but NOT the budget
consumed — that is set
+ // entirely by the caller's epsilon parameter.
+ // A release at epsilon=1.0 costs more budget than one at
epsilon=0.5.
+ DPBudgetAccountant acc1 = new DPBudgetAccountant(100.0, 1e-5);
+ DPBudgetAccountant acc2 = new DPBudgetAccountant(100.0, 1e-5);
+ acc1.compose(0.5, 0.0, 1.0); // epsilon=0.5, Laplace
+ acc2.compose(1.0, 0.0, 1.0); // epsilon=1.0, same sensitivity
+
+ Assert.assertTrue("Higher epsilon costs more budget (Laplace
basic composition)",
+ acc1.totalEpsilonSpent() < acc2.totalEpsilonSpent());
+ }
+
+ // --- Constructor error paths ------------------------------------
+
+ @Test(expected = DMLRuntimeException.class)
+ public void testConstructorRejectsZeroEpsilonBudget() {
+ new DPBudgetAccountant(0.0, 1e-5);
+ }
+
+ @Test(expected = DMLRuntimeException.class)
+ public void testConstructorRejectsNegativeEpsilonBudget() {
+ new DPBudgetAccountant(-0.5, 1e-5);
+ }
+
+ @Test(expected = DMLRuntimeException.class)
+ public void testConstructorRejectsDeltaZero() {
+ new DPBudgetAccountant(1.0, 0.0);
+ }
+
+ @Test(expected = DMLRuntimeException.class)
+ public void testConstructorRejectsDeltaOne() {
+ new DPBudgetAccountant(1.0, 1.0);
+ }
+
+ //
=======================================================================
+ // 1b. DMLProgram / ExecutionContext.getDPBudgetAccountant()
(dp_set_budget)
Review Comment:
'1b.'?
##########
src/test/java/org/apache/sysds/test/functions/privacy/dp/DPBuiltinDMLTest.java:
##########
@@ -0,0 +1,269 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.test.functions.privacy.dp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.HashMap;
+
+import org.apache.sysds.parser.LanguageException;
+import org.apache.sysds.parser.ParseException;
+import org.apache.sysds.runtime.DMLRuntimeException;
+import org.apache.sysds.runtime.matrix.data.MatrixValue.CellIndex;
+import org.apache.sysds.test.AutomatedTestBase;
+import org.apache.sysds.test.TestConfiguration;
+import org.apache.sysds.test.TestUtils;
+import org.junit.Test;
+
+/*
+ * ==========================================================================
+ * DML integration test
+ * ==========================================================================
+ *
+ * Full integration tests extend AutomatedTestBase and drive the DML runner.
+ * Each test:
+ * (a) Writes a DML script to a temp file.
Review Comment:
Create the DML scripts directly as files under test/scripts, similar to
other unit tests.
##########
src/main/python/requirements.txt:
##########
@@ -0,0 +1,31 @@
+#-------------------------------------------------------------
Review Comment:
This file should be removed.
##########
src/test/java/org/apache/sysds/test/component/cp/DPBuiltinCPInstructionTest.java:
##########
@@ -0,0 +1,409 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.test.component.cp;
+
+import org.apache.sysds.parser.DMLProgram;
+import org.apache.sysds.runtime.DMLRuntimeException;
+import org.apache.sysds.runtime.controlprogram.Program;
+import org.apache.sysds.runtime.controlprogram.context.ExecutionContext;
+import org.apache.sysds.runtime.controlprogram.context.ExecutionContextFactory;
+import org.apache.sysds.runtime.privacy.dp.DPBudgetAccountant;
+
+import org.junit.Test;
+import org.junit.Assert;
+
+/**
+ * Tests for {@code DPBuiltinCPInstruction} and {@code DPBudgetAccountant}.
+ *
+ * The tests are grouped into three levels: - Unit tests on DPBudgetAccountant
— verify composition, conversion, and
+ * budget enforcement in isolation, with no dependency on the full SystemDS
runtime. - Noise distribution tests — verify
+ * that the noise blocks generated by the Laplace and Gaussian mechanisms have
statistically correct means and variances
+ * (Kolmogorov-Smirnov style sanity checks). - DML integration tests</b> — run
complete DML scripts and verify
Review Comment:
Remove html notation here (i.e., '</b>')
##########
src/main/java/org/apache/sysds/parser/DMLTranslator.java:
##########
@@ -2589,6 +2589,53 @@ else if (
sop.equalsIgnoreCase(Opcodes.NOTEQUAL.toString()) )
case DECOMPRESS:
currBuiltinOp = new UnaryOp(target.getName(),
target.getDataType(), ValueType.FP64, OpOp1.DECOMPRESS, expr);
break;
+ case DP_LAPLACE: {
Review Comment:
Move these cases to the method
`processParameterizedBuiltinFunctionExpression` above to simplify the parameter
parsing.
##########
src/main/java/org/apache/sysds/runtime/instructions/cp/DPBuiltinCPInstruction.java:
##########
@@ -0,0 +1,369 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.runtime.instructions.cp;
+
+import org.apache.sysds.runtime.DMLRuntimeException;
+import org.apache.sysds.runtime.controlprogram.context.ExecutionContext;
+import org.apache.sysds.runtime.instructions.InstructionUtils;
+import org.apache.sysds.runtime.matrix.data.LibMatrixMult;
+import org.apache.sysds.runtime.matrix.data.LibMatrixReorg;
+import org.apache.sysds.runtime.matrix.data.MatrixBlock;
+import org.apache.sysds.runtime.privacy.dp.DPBudgetAccountant;
+
+import java.util.LinkedHashMap;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * CP instruction for differential-privacy release of a linear query over the
original matrix.
+ *
+ * DML syntax (raw-matrix form): result = dp_laplace(X, query="colMeans",
sensitivity=1.0, epsilon=0.5) result =
+ * dp_gaussian(X, query="colMeans", sensitivity=1.0, epsilon=0.5, delta=1e-5)
+ *
+ * The instruction receives the original {@code n x d} matrix {@code X},
builds a transformation matrix {@code T}
+ * ({@code k x n}) from the named {@code query} (see {@link #buildTransform}),
and returns a noisy release of
+ * {@code T %*% X}. The noise is not added as a separate elementwise pass over
a materialised aggregate: it is injected
+ * by augmenting {@code T} with an identity block and {@code X} with the noise
matrix, so that the noisy release is the
+ * result of a single {@link LibMatrixMult#matrixMult} call (see {@link
#processInstruction} for the derivation).
+ *
+ * Sensitivity norm: {@code sensitivity} is not interchangeable between the
two builtins. {@code dp_laplace} calibrates
+ * its noise scale to the L1 sensitivity of {@code T %*% X} to a single-record
change; {@code dp_gaussian} calibrates
+ * its σ to the L2 sensitivity. For a scalar release (e.g. {@code
query="colMeans"} on single-column {@code X}) the two
+ * norms coincide, but for a vector- or matrix-valued release they generally
differ — the caller is responsible for
+ * supplying the norm matching the builtin invoked (see {@link
#sensitivityOf}).
+ *
+ * The {@link #sensitivityOf} method is deliberately separated from the
noise-scale computation. It currently returns
+ * the caller-supplied constant. A future rewrite pass could replace the body
of this single method with a static
+ * analysis that derives sensitivity from {@code T}'s column norms and a
declared per-record bound on {@code X}; every
+ * other line in this class would stay unchanged.
+ */
+public class DPBuiltinCPInstruction extends ComputationCPInstruction {
Review Comment:
Can we extend from `ParameterizedBuiltinCPInstruction`, since the DP
builtins belong to the parameterized builtin functions?
##########
src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java:
##########
@@ -2003,8 +2003,62 @@ else if(this.getOpCode() == Builtins.MAX_POOL ||
this.getOpCode() == Builtins.AV
output.setBlocksize (id.getBlocksize());
output.setValueType(id.getValueType());
}
- else
+ else
raiseValidateError("Local instruction not
allowed in dml script");
+ break;
+ case DP_LAPLACE: {
+ checkNumParameters(4);
+ checkMatrixParam(getFirstExpr());
+ checkScalarParam(getSecondExpr());
+ checkValueTypeParam(getSecondExpr(), ValueType.STRING);
+ checkScalarParam(getThirdExpr());
+ checkScalarParam(getFourthExpr());
+ String dpLaplaceQuery =
getDPQueryLiteral(getSecondExpr());
+ long[] dpLaplaceDims = getDPOutputDims(dpLaplaceQuery,
+ getFirstExpr().getOutput().getDim1(),
getFirstExpr().getOutput().getDim2());
Review Comment:
Since these two functions are always called together, can we merge them to
have a single function call to get the output dimensions?
##########
src/test/java/org/apache/sysds/test/component/cp/DPBuiltinCPInstructionTest.java:
##########
@@ -0,0 +1,409 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.test.component.cp;
+
+import org.apache.sysds.parser.DMLProgram;
+import org.apache.sysds.runtime.DMLRuntimeException;
+import org.apache.sysds.runtime.controlprogram.Program;
+import org.apache.sysds.runtime.controlprogram.context.ExecutionContext;
+import org.apache.sysds.runtime.controlprogram.context.ExecutionContextFactory;
+import org.apache.sysds.runtime.privacy.dp.DPBudgetAccountant;
+
+import org.junit.Test;
+import org.junit.Assert;
+
+/**
+ * Tests for {@code DPBuiltinCPInstruction} and {@code DPBudgetAccountant}.
+ *
+ * The tests are grouped into three levels: - Unit tests on DPBudgetAccountant
— verify composition, conversion, and
+ * budget enforcement in isolation, with no dependency on the full SystemDS
runtime. - Noise distribution tests — verify
+ * that the noise blocks generated by the Laplace and Gaussian mechanisms have
statistically correct means and variances
+ * (Kolmogorov-Smirnov style sanity checks). - DML integration tests</b> — run
complete DML scripts and verify
+ * end-to-end correctness via the existing AutomatedTestBase machinery.
+ *
+ * The DML integration tests require a built SystemDS jar and are separated
into a companion class
+ * {@link org.apache.sysds.test.functions.privacy.dp.DPBuiltinDMLTest}.
+ */
+public class DPBuiltinCPInstructionTest {
+
+ private static final double EPS = 1e-9;
+
+ //
=======================================================================
+ // 1. DPBudgetAccountant unit tests
Review Comment:
What does the '1.' refer to?
##########
pom.xml:
##########
@@ -408,6 +408,7 @@
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
+ <reportFormat>plain</reportFormat>
Review Comment:
What is the reason for this change? What is it needed for?
##########
src/test/java/org/apache/sysds/test/component/cp/DPBuiltinCPInstructionTest.java:
##########
@@ -0,0 +1,409 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.test.component.cp;
+
+import org.apache.sysds.parser.DMLProgram;
+import org.apache.sysds.runtime.DMLRuntimeException;
+import org.apache.sysds.runtime.controlprogram.Program;
+import org.apache.sysds.runtime.controlprogram.context.ExecutionContext;
+import org.apache.sysds.runtime.controlprogram.context.ExecutionContextFactory;
+import org.apache.sysds.runtime.privacy.dp.DPBudgetAccountant;
+
+import org.junit.Test;
+import org.junit.Assert;
+
+/**
+ * Tests for {@code DPBuiltinCPInstruction} and {@code DPBudgetAccountant}.
+ *
+ * The tests are grouped into three levels: - Unit tests on DPBudgetAccountant
— verify composition, conversion, and
+ * budget enforcement in isolation, with no dependency on the full SystemDS
runtime. - Noise distribution tests — verify
+ * that the noise blocks generated by the Laplace and Gaussian mechanisms have
statistically correct means and variances
+ * (Kolmogorov-Smirnov style sanity checks). - DML integration tests</b> — run
complete DML scripts and verify
+ * end-to-end correctness via the existing AutomatedTestBase machinery.
+ *
+ * The DML integration tests require a built SystemDS jar and are separated
into a companion class
+ * {@link org.apache.sysds.test.functions.privacy.dp.DPBuiltinDMLTest}.
+ */
+public class DPBuiltinCPInstructionTest {
+
+ private static final double EPS = 1e-9;
+
+ //
=======================================================================
+ // 1. DPBudgetAccountant unit tests
+ //
=======================================================================
+
+ @Test
+ public void testAccountantInitialisesAtZeroCost() {
+ DPBudgetAccountant acc = new DPBudgetAccountant(1.0, 1e-5);
+ // No releases yet: total cost should be a large negative number
+ // (conversion formula gives -∞ when rdpSum = 0 for all orders),
+ // so remainingBudget() should exceed the budget.
+ Assert.assertTrue("No releases should leave budget intact",
acc.remainingBudget() > 0);
+ Assert.assertEquals(0, acc.releaseCount());
+ }
+
+ @Test
+ public void testSingleLaplaceReleaseDoesNotExceedBudget() {
Review Comment:
Try to avoid having numerous small unit test: Some of these test cases can
be merged into a single function.
Here for example, the asserts from the first test can be moved in-between
the lines from the second test.
Of course, there should be a good balance between squeezing everything into
a single function and testing every command separately.
##########
src/test/java/org/apache/sysds/test/functions/privacy/dp/DPBuiltinDMLTest.java:
##########
@@ -0,0 +1,269 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.test.functions.privacy.dp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.HashMap;
+
+import org.apache.sysds.parser.LanguageException;
+import org.apache.sysds.parser.ParseException;
+import org.apache.sysds.runtime.DMLRuntimeException;
+import org.apache.sysds.runtime.matrix.data.MatrixValue.CellIndex;
+import org.apache.sysds.test.AutomatedTestBase;
+import org.apache.sysds.test.TestConfiguration;
+import org.apache.sysds.test.TestUtils;
+import org.junit.Test;
+
+/*
+ * ==========================================================================
+ * DML integration test
+ * ==========================================================================
+ *
+ * Full integration tests extend AutomatedTestBase and drive the DML runner.
+ * Each test:
+ * (a) Writes a DML script to a temp file.
+ * (b) Provides input matrices via TestUtils.
Review Comment:
Use the method `getRandomMatrix()` from `AutomatedTestBase` for generating
random test matrices.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]