Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
682c83e
add: nSVT.v
Jun 2, 2026
04b1b67
fix: README
Jun 2, 2026
4e5d5f5
proof_cpl: nAT spec
Jun 3, 2026
262a14f
add: def+spec+proof onSVT
Jun 4, 2026
28e2b1a
fix: nSVT, right coeffs
Jun 4, 2026
1e73f5e
rename & clean: nSVT.v -> numeric_sparse_vector_technique.v
Jun 8, 2026
375c002
nSVT interpreted but not compiled
Jun 8, 2026
d3621b2
add: branch for dev / for compile, pmw
Jun 9, 2026
90c1bed
compiled ?
Jun 9, 2026
db2713a
fix: option Z and add the stream client + proof
Jun 10, 2026
17c55a8
merge & end: numeric_sparse_vector_technique
Jun 10, 2026
8af8acb
clean: nSVT
Jun 10, 2026
f552447
add: OCaml implementation of nSVT, 1/epsilon
Jun 10, 2026
39aa42e
add: OCaml nSVT implementation
Jun 10, 2026
6573d29
add: nSVT main.ml intermediate epsilon
Jun 10, 2026
c9d3edd
add: pmw OCaml implementation, no good tests yet
Jun 11, 2026
e8d2c96
clean: numeric_sparse_vector_technique.v
Jun 11, 2026
90c7546
fix: srcs nsvt & pmw
Jun 15, 2026
6c7e4c1
add: pmw.v
Jun 15, 2026
bbeb919
pmw.ml: results, not what expected but still RESULTSgit add db_sampli…
Jun 16, 2026
56145a9
format: implem OCaml
Jun 16, 2026
10871d7
clean: rename main in numeric_spares_vector implem
Jun 17, 2026
b5c42a6
add: README for the ocaml implementation of nSVT and PMW
Jun 17, 2026
b285efd
fix: README implem OCaml
Jun 17, 2026
57c27c3
fix: ref README implem OCaml
Jun 17, 2026
799418b
fix: all ref README implem OCaml
Jun 17, 2026
6f0814b
fix: titles README implem OCaml
Jun 17, 2026
40884f1
fix: math README.md
Jun 17, 2026
a38b101
fix: 2 math README.md
Jun 17, 2026
ea1e7be
pmw.v: befor big change
Jun 19, 2026
d764b33
implem pmw: fixs to stay the same as the formal algo
Jun 19, 2026
d707929
before change in the algorithm
Jun 24, 2026
2b725ce
Issue: q distrib in pMW.v
Jun 24, 2026
53252f2
Before breaking changes: pmw.ml
Jun 24, 2026
6fecc1d
after change: still issue on weird convergence of the distribution
Jun 24, 2026
de49a33
add: data for ocaml implem
Jun 24, 2026
f6682a3
add: implem ocaml, db utils and gen db
Jun 24, 2026
a347c07
Convergence: almost ok, still issue on threshold
Jun 25, 2026
874aed7
clean: data src
Jun 25, 2026
28d35e5
add: README data src
Jun 25, 2026
811c490
fix: READMEs src
Jun 25, 2026
4f17c46
almost working: src pmw (still issue with threshold + matches with reame
Jun 25, 2026
5336ae0
fix: precision->size pmw
Jun 25, 2026
06fd4ce
clean & factorisation: implementation OCaml
Jun 25, 2026
1d8a8ac
pmw data: change in the distribution
Jun 25, 2026
c3c4d94
fix: wrong normalization in mk_histo
Jun 25, 2026
8b47e37
change name nsvt -> pmw
Jun 25, 2026
95849bb
proof: before change in algo
Jun 25, 2026
0de22d9
add: gif representation of the evolution of the distrib
Jun 25, 2026
76a7238
clear: jpg for gif
Jun 25, 2026
a764a4a
add: data for gif
Jun 25, 2026
c42cf2a
clean: data for gif
Jun 25, 2026
0634480
add: readme for pmw ml gif
Jun 25, 2026
0c0b29f
clean: nsvt.v
Jun 26, 2026
82dc09b
determinism of q in use: still an issue with upd _ db _
Jun 26, 2026
0eb4265
fix: main_pmw.ml, no gif.
Jun 26, 2026
a947248
fix: readme pmw
Jun 26, 2026
39bccba
PMW: First proof with qed
Jun 26, 2026
072bace
fix: pmw, add 1sens hyp for f1 & f2
Jun 26, 2026
2e89764
src: Update README
Jul 2, 2026
6ff64de
src: major clean
Jul 2, 2026
eb10afe
fix: gen_data -> str, render -> everyting is automatic
Jul 6, 2026
0b62172
list: implementation as in rocq
Jul 6, 2026
1f4e648
update: REAMES automated calls
Jul 6, 2026
67697fa
modif: distrib + gif
Jul 6, 2026
9862253
clean + adapt coefs src
Jul 6, 2026
0fa9ce4
try: add gif in readme
Jul 6, 2026
0a3fd95
src: list part in int lists
Jul 6, 2026
46891e1
src: gif render linear and not constant clip for the y axis
Jul 6, 2026
777f793
src: utils clean
Jul 6, 2026
70f485a
add: src, description of --list
Jul 6, 2026
409160b
fix: proof for pmw with lists
Jul 8, 2026
364c83f
fix: src render + readme
Jul 9, 2026
fced8cb
upd: src alpha + render + evolution gif
Jul 9, 2026
8d72636
fix: src, gif opti
Jul 9, 2026
f556d43
fix: src render time
Jul 10, 2026
e5d795e
fix: src var name card_q -> log_card_q
Jul 10, 2026
71a5f7f
proof: before change in the compute query
Jul 13, 2026
74ef536
src: c_query as in rocq
Jul 13, 2026
e425a51
src: anum aden ... manipulates integers
Jul 15, 2026
0c1cb2b
proof: almost complete 4 same admit and some lemmas assumed, won't ha…
Jul 17, 2026
e7c3ae8
src: Heterogenous Data
Jul 17, 2026
82a7675
src: add gif 1 readme
Jul 17, 2026
f634619
src: overestimation issue
Jul 17, 2026
3dc67d3
src: fix readmes
Jul 17, 2026
a1c6345
src: illustration of issue of the list implementation
Jul 17, 2026
992935a
src: fix readme
Jul 17, 2026
dbd6023
src: readme add source
Jul 17, 2026
c640ab2
fix: issue threhold ?
Jul 17, 2026
2e66c6b
src: fix readme
Jul 17, 2026
9e69b8a
src: fix readme
Jul 17, 2026
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 README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
This is a fork of the clutch project in which I will do my M1 internship work.

# Clutch Project

This repository contains the formal development of a number of higher-order probabilistic separation logics for proving properties of higher-order probabilistic programs.
Expand Down
105 changes: 105 additions & 0 deletions src/diffpriv/private_multiplicative_weights/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Private Multiplicative Weights

In this folder you will find an OCaml implementation of:
- Numeric Sparse Vector technique
- Private Multiplicative Weights

The programs [`main_nsvt.ml`](https://github.com/Pbi0/clutch/blob/pMW_formal/src/diffpriv/numeric_sparse_vector/main_nsvt.ml)
and [`main_pmw.ml`](https://github.com/Pbi0/clutch/blob/pMW_formal/src/diffpriv/numeric_sparse_vector/main_pmw.ml)
are tests calling the two methods.

## nSVT

The numeric sparse vector technique is an algorithm which given:
- A privacy parameter (`num`, `den`)
- A threshold (`t`)
- The maximum number of numeric answers (`n`)

Outputs a function which given a database (`db`) and a query (`qi`) outputs:
- `None`, if the value returned by the query on the db is under `t` or if it has
already answered more than `n` numeric values.
- `Some (v)` with `v`$\in \mathbb(Z)$ otherwise.

The `nSVT_stream` is a client which given a stream of queries (represented by a
function which computes the next query adaptively to the answers) returns the
list of the answers while preserving the $\varepsilon$-DP.

## PMW

The private multiplicative weights is a mechanism which given a database and
some privacy and accuracy parameters returns a function which one can call
with an adaptive stream of queries and get each time a numeric answer.

This algorithm is working. There is just a point to raise.
The implementation is derived from C.Dwork book on differential privacy (!!!cite better),
in which the threshold is computed in order to match an accuracy statement.
Since we are working on small databases, the threshold often get really high.
Then very few updates are made and the execution is not interesting.
Then we can lower manually the threshold to get an interesting output.
Even if it does change something about the accuracy statement, it do
not change anything for the privacy one.
That is why we are making this choice. Then the threshold will need
to be modified depending on the database under study.

For the [`main_pmw.ml`](https://github.com/Pbi0/clutch/blob/pMW_formal/src/diffpriv/numeric_sparse_vector/main_pmw.ml)
example, we consider the database that are randomly generated [`rd_data.csv`](https://github.com/Pbi0/clutch/blob/pMW_formal/src/diffpriv/numeric_sparse_vector/data/rd_data.csv)
.
Or the databases from ... (!!!push and cite the database.)

In order to manage the databases, inputs, outputs and histograms we uses
functions defined in
[`utils.ml`](https://github.com/Pbi0/clutch/blob/pMW_formal/src/diffpriv/numeric_sparse_vector/utils.ml).

When calling:
```bash
ocaml main_pmw.ml
```

The output is, the initial database and the approached (sanitized) database as
well as the list (of the distances between the real answer and the returned
answer to the stream of queries) *see what to put in the final version*.

You can add the three optional following arguments:
- `--file [file]` in order to specify the database to use
- `--list` use the list implementation.
- `--gif` in order to save the databases in a folder and then to be able to
build a graphical representation of the evolution of the sanitized database
during the update process.

For the `--list` option, instead of using the hastable implementation, it uses
lists. It is the translation of what is written in rocq into ocaml.
It then uses a natural (int) distribution / it is not normalized.

## DATA

To get the data, you can go to [`data/`](https://github.com/Pbi0/clutch/tree/pMW_formal/src/diffpriv/private_multiplicative_weights/data).

## GIF

If you want to have a gif illustration of the distribution evolution during
the pmw, you can add the argument `--gif` when calling
[`main_pmw.ml`](https://github.com/Pbi0/clutch/blob/pMW_formal/src/diffpriv/numeric_sparse_vector/main_pmw.ml)
and then can go to [`gif/`](https://github.com/Pbi0/clutch/tree/pMW_formal/src/diffpriv/private_multiplicative_weights/gif)
where you will find in the folder `gif/data/` all the distribution that $h$
went through. The 0 database is the real database.
In order to get the gif, go in the `gif/` folder and run:
```bash
python render_gif.py
```
or
```bash
./render_gif.py
```

It will build the gif under the name of `evolution_distrib.gif`.
What follows is a output of this function.\
You can find the parameters for this
output in [gif/ref_evolution/](https://github.com/Pbi0/clutch/blob/pMW_formal/src/diffpriv/private_multiplicative_weights/gif/ref_evolution/).

![Evolution of the distribution.](https://github.com/Pbi0/clutch/blob/pMW_formal/src/diffpriv/private_multiplicative_weights/gif/ref_evolution/evolution_distrib.gif?raw=true "Evolution of the distribution.")

## NOTES

We use the probability sampler from [`noiseSampling.ml`](https://github.com/Pbi0/clutch/blob/pMW_formal/src/diffpriv/numeric_sparse_vector/noiseSampling.ml)
which is a truncated part
of the file [`../differential_privacy.ml`](https://github.com/Pbi0/clutch/blob/pMW_formal/src/diffpriv/differential_privacy.ml).
23 changes: 23 additions & 0 deletions src/diffpriv/private_multiplicative_weights/data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# DATA

In this folder you will find a python script which generate random data.
You can execute it with the following command:
```bash
python gen_data.py
```

## The script

In the current version the script writes in the file `rd_data.csv` a random
sequence of integers between 0 and 9 with a probability of $\frac{1}{4}$ for 1,
$\frac{1}{16}$ for 2 and so on (exponential) and stops at 12.

## Other data

Other kind of data can be used.
As the algorithm uses strings as key for the database any file could
be a database. However the bigger is the domain / the number of different
records in the database / the number of different lines in the file,
the bigger the number of query will be and then the longer it will take.
The execution time is exponential in the size of the domain.

21 changes: 21 additions & 0 deletions src/diffpriv/private_multiplicative_weights/data/gen_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/python3

from random import randint


if __name__ == "__main__":
size = 1_000_000
nb_o = 13

with open("rd_data.csv", "w", encoding="utf-8") as f:
for i in range(size):
token = True
count = 0
while token and count < nb_o:
if randint(0, 3) == 0:
f.write(str(count)+"\n")
token = False
count += 1
if token:
f.write(str(nb_o)+"\n")

35 changes: 35 additions & 0 deletions src/diffpriv/private_multiplicative_weights/gif/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# GIF

In this folder there are files allowing you to build a gif illustration of the
evolution of the distribution during the `pMW`.

Once you have called `main_pmw.ml` with the argument `--gif` then you will
have all the databases that the distribution $h$ went through (stored in `data/`).
The $0^{th}$ database is the real distribution.
In order to get the gif, run:
```bash
python render_gif.py
```
or (if you run before `chmod +x render_gif.py`)
```bash
./render_gif.py
```

The `DB` histogram represents the real database while the `sDB` histogram
represents the sanitized database. It is the one evolving during the process.
I the initial database for `sDB` is the uniforme database then there should not
have any problem with the axis. However since they are clipped on the values of
the real database, the histograms might sometimes overpass the view (but it is
really unlikely)

To do something perfect where the axis are clipped and where everything is in
the view, we should range twice each database to first find the global maximum
and then to build the plots. We prefer clipping the axis based on `DB` which
is fixed and who should be the histogram towards which tends `sDB`.

# Illustration of the issue that comes with the lists implementation

In [`heterogenous_database`](https://github.com/Pbi0/clutch/tree/pMW_formal/src/diffpriv/private_multiplicative_weights/gif/heterogenous_database/)link to the folder you can find several evolutions and the parameters
associated. It illustrates the issue we encountered with the list
implementation.

Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Heterogenous Database

In this folder you can find the representation of the evolution of
$h$ on a database with a huge domain and the distribution over it
is far from beeing uniform.

The data under study are from the [programming dp book](https://github.com/uvm-plaid/programming-dp/blob/master/notebooks/adult_with_pii.csv).

## Evolution with the probability distribution (Hasthbl. implmentation)

With the following parameters :

| Parameter | Value |
|:--------------|-----------:|
| $\varepsilon$ | $1$ |
| $\alpha$ | $1/10$ |
| $\beta$ | $1/10$ |
| $\eta$ | $\alpha/2$ |

We get the following evolution :

![Evolution of the distribution.](https://github.com/Pbi0/clutch/blob/pMW_formal/src/diffpriv/private_multiplicative_weights/gif/heterogenous_database/evolution_distrib.gif?raw=true "Evolution of the distribution.")

We don't have the issue in the normalizing step.

## Evolution with the count histograms (List. implementation)

With the following parameters :

| Parameter | Value |
|:--------------|-----------:|
| $\varepsilon$ | $1$ |
| $\alpha$ | $1/100$ |
| $\beta$ | $1/100$ |
| $\eta$ | $\alpha$/2 |

We get the following evolution :

![Evolution of the distribution.](https://github.com/Pbi0/clutch/blob/pMW_formal/src/diffpriv/private_multiplicative_weights/gif/heterogenous_database/evolution_distrib_issue.gif?raw=true "Evolution of the distribution with overestimation of the first elements.")

We can see the issue in the scaling step.
The firsts elements are overestimated and
some elements (the small ones) are underestimated.

While with the following parameters :

| Parameter | Value |
|:--------------|-----------:|
| $\varepsilon$ | $1$ |
| $\alpha$ | $1/100$ |
| $\beta$ | $1/100$ |
| $\eta$ | $6*\alpha$ |

We get the following evolution :

![Evolution of the distribution.](https://github.com/Pbi0/clutch/blob/pMW_formal/src/diffpriv/private_multiplicative_weights/gif/heterogenous_database/evolution_distrib_adapted_factor.gif?raw=true "Evolution of the distribution with an addapted learning factor.")

We can see that there is no longer overestimation and that the convergence is
faster as well. However there is more gittering (it is less precise than the
first example using the Hastbl. implementation.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Reference evolution

This evolution is the one on the database that is generated randomly.
It uses the List. implementation.
We see that it converges.

The parameters are the following:

| Parameter | Value |
|:--------------|-----------:|
| $\varepsilon$ | $1$ |
| $\alpha$ | $1/100$ |
| $\beta$ | $1/100$ |
| $\eta$ | $\alpha/2$ |


![Evolution of the distribution.](https://github.com/Pbi0/clutch/blob/pMW_formal/src/diffpriv/private_multiplicative_weights/gif/ref_evolution/evolution_distrib.gif?raw=true "Evolution of the distribution.")
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 65 additions & 0 deletions src/diffpriv/private_multiplicative_weights/gif/render_gif.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/python3

import csv
import matplotlib.pyplot as plt
from PIL import Image
import io

def max_l(l):
a = 0
for x in l:
if a < x:
a = x
return a

fps = 10
ts = 20


if __name__ == "__main__":
images = []

with open("data/len.csv", "r") as flen:
datalen = csv.reader(flen)
nb_images = 0
for row in datalen:
nb_images = int(row[0])
step = nb_images/(fps*ts)
with open("data/gif0.csv", "r") as f0:
data0 = csv.reader(f0)
x0 = []
y0 = []
max_d = 0
for row in data0:
#x0.append(int(row[0]))
x0.append(row[0])
y0.append(float(row[1]))
if max_d < float(row[1]):
max_d = float(row[1])
for i in range(0, nb_images, int(step)):
#if i % int(step) == 0:
# print(str(int(1000*i/nb_images)/10) + "%", "\r", end="")
print(str(int(1000*i/nb_images)/10) + "%", "\r", end="")
with open(f"data/gif{i+1}.csv", "r") as f:
data = csv.reader(f)
x = []
y = []
for row in data:
#x.append(int(row[0]))
x.append(row[0])
y.append(float(row[1]))
fig, ax = plt.subplots()
ax.bar(x0, y0, color='yellow', alpha=0.8, label='DB')
ax.bar(x, y, color='cyan', alpha=0.5, label='sDB')
ax.set_ylim(0, max_d+max_d/10)
plt.legend(loc='upper left')

buf = io.BytesIO()
fig.savefig(buf, format='png')
buf.seek(0)
images.append(Image.open(buf))
plt.close()
print("exporting")
images[0].save('evolution_distrib.gif',
save_all=True, append_images=images[1:],
optimize=False, duration=5*ts)
59 changes: 59 additions & 0 deletions src/diffpriv/private_multiplicative_weights/main_nsvt.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#use "numeric_sparse_vector.ml"

let path = "data/"
let file = "data0"
let () = Random.self_init ()

let mk_array file =
(* turn a column file of numbers into a int Array *)
let reader = open_in (path ^ file) in
let rec aux reader acc =
try
let line = input_line reader in
aux reader (string_to_int line :: acc)
with e ->
close_in_noerr reader;
acc
in
Array.of_list (aux reader [])

let count_above t l =
Array.fold_left (fun x y -> x + if y > t then 1 else 0) 0 l

let () =
let db = mk_array file in
let stream_query =
let a = ref 1000 in
fun bs ->
if !a <= 0 then None
else (
a := !a - 1;
Some (count_above !a))
in
Printf.printf "\nSmall epsilon -->\n";
let res = nSVT_stream 1 30 200 20 stream_query db in
aff_l_op res;

let stream_query =
let a = ref 1000 in
fun bs ->
if !a <= 0 then None
else (
a := !a - 1;
Some (count_above !a))
in
Printf.printf "\nMedium (=1) epsilon -->\n";
let res = nSVT_stream 1 1 200 20 stream_query db in
aff_l_op res;

let stream_query =
let a = ref 1000 in
fun bs ->
if !a <= 0 then None
else (
a := !a - 1;
Some (count_above !a))
in
Printf.printf "\nLarge epsilon -->\n";
let res = nSVT_stream 30 1 200 20 stream_query db in
aff_l_op res
Loading