Skip to content

Commit

Permalink
more work on Rust-based ΔQ (#30)
Browse files Browse the repository at this point in the history
* remove artificial binning from CDF

in preparation for parsing from text, which shouldn’t care about binning

* add CDF compaction

* test CDF FromStr

* update README with progress

* remove some debug code

* stash parser

* fix parsing

* add initial text editing

* make editing mostly work, update README

* use right max_size/mode in eval

* add exponentiation

* make things cheaper to clone
  • Loading branch information
rkuhn authored Oct 1, 2024
1 parent 82eff55 commit 088d46d
Show file tree
Hide file tree
Showing 11 changed files with 1,125 additions and 251 deletions.
23 changes: 22 additions & 1 deletion delta_q/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions delta_q/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ js-sys = { version = "0.3.70", optional = true }
parking_lot = { version = "0.12.3", optional = true }
serde = { version = "1.0.210", features = ["derive"] }
serde_json = { version = "1.0.128", optional = true }
smallstr = { version = "0.3.0", features = ["std", "serde"] }
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = [
"env-filter",
], optional = true }
wasm-bindgen = { version = "0.2.93", optional = true }
wasm-bindgen-futures = { version = "0.4.43", optional = true }
winnow = "0.6.20"
yew = { version = "0.21.0", features = ["csr"], optional = true }

[dependencies.web-sys]
Expand Down
11 changes: 3 additions & 8 deletions delta_q/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ The underlying ΔQ expressions shall be used for export/import of the model, com

## Implementation plan

The first step is to provide a library for manipulating CDFs, offering all operations required by the theory; the internal representation will be discrete numerical [DONE], with later optimizations for constant parts at the beginning and end of the vector.
The first step is to provide a library for manipulating CDFs, offering all operations required by the theory; the internal representation will be discrete numerical [DONE], with later optimizations for constant parts at the beginning and end of the vector. [DONE]

The second step yields an internal DSL for creating ΔQ expressions and printing them. [DONE]

The third step provides evaluation of ΔQ expressions as defined in the paper. [DONE]
This will later be expanded to include some exponentiation-like operator that simplifies expressing a randomly chosen repetition count for some sub-expression (as frequently occurs in gossip protocols).
This will later be expanded to include some exponentiation-like operator that simplifies expressing a randomly chosen repetition count for some sub-expression (as frequently occurs in gossip protocols). [DONE]

The fourth step adds a web UI to expose the internal DSL to no-code users. [DONE]
The interaction with a ΔQ expression shall closely resemble the refinement approach for system modelling as defined in the paper. [DONE]
Expand All @@ -32,7 +32,7 @@ The results of the load analysis will indicate where and under which conditions
The build comprises two steps:

- `trunk build` (i.e. you’ll need to `cargo install --locked trunk` first)
- `cargo run --bin editor`
- `cargo run --bin editor -F main`

The first one uses [trunk](https://trunkrs.dev) to build the web app in the `dist/` folder, which the second one then integrates into the single-binary application that will serve HTTP resources on port 8080 when run.

Expand All @@ -57,11 +57,6 @@ rustup target add wasm32-unknown-unknown

## Known Shortcomings

- not optimised at all, especially regarding memory usage (need to make cloning cheap for CDF, DeltaQ, etc.) and web assembly size
- functional but ugly
- duplicates state management in web app and backend, not yet decided what to put where (currently ΔQ expression evaluation is done in the backend, could easily move to a web worker)
- no editing of CDFs yet
- should have export (probably as JSON) and matching import
- should allow editing the formulas as text, which requires making the syntax more accessible via normal keyboards
- fixed samples and width for CDF, should be changed to support any step function and compute its width dynamically (with upper bound on details to avoid memory explosion)
- should add “exponentiation” operator to wrap an expression in another one with a hole for a specified number of times
89 changes: 77 additions & 12 deletions delta_q/src/bin/editor-web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ fn app_main() -> HtmlResult {
};

let selected = use_state(|| Some("out".to_owned()));
let onclick = cloned!(selected; Callback::from(move |n| selected.set(Some(n))));
let select = cloned!(selected; Callback::from(move |n| selected.set(Some(n))));

// epoch counter to trigger recomputation when the context changes
let epoch = use_state(|| 0);
Expand Down Expand Up @@ -123,18 +123,21 @@ fn app_main() -> HtmlResult {
.iter()
.map(|(k, v)| {
let name = k.clone();
let onclick = onclick.clone();
let mut h = html! {
<li>
<button onclick={cloned!(name, on_change; move |_| on_change.emit((name.clone(), None)))}>{ "delete "}</button>
<span class={classes!("expression")} style="margin-left: 8px;" onclick={onclick.reform(move |_| name.clone())}>{ format!("{k}: {v}") }</span>
</li>
};
if selected.as_ref() == Some(k) {
let select = select.clone();
let sel = if selected.as_deref() == Some(k.as_str()) {
sel_found = true;
h = html! { <strong>{ h }</strong> };
Some("selected")
} else {
None
};
html! {
<li class={classes!("row")}>
<button onclick={cloned!(name, on_change; move |_| on_change.emit((name.to_string(), None)))}>{ "delete "}</button>
<div class={classes!("expression", sel)} style="margin-left: 8px;" onclick={select.reform(move |_| name.to_string())}>
{ k }{ ": " }<EditExpression name={k.to_string()} value={v.clone()} on_change={on_change.clone()} selected={sel.is_some()} />
</div>
</li>
}
h
})
.collect::<Html>();
if selected.is_some() && !sel_found {
Expand All @@ -143,7 +146,7 @@ fn app_main() -> HtmlResult {
}

let dq = selected.as_ref().and_then(|name| ctx.get(name));
web_sys::console::log_1(&JsValue::from_str(&format!("{dq:?}")));
// web_sys::console::log_1(&JsValue::from_str(&format!("{dq:?}")));

let cdf = match cdf {
Ok(cdf) => cdf_to_svg(&cdf),
Expand Down Expand Up @@ -202,6 +205,68 @@ fn add_expression(props: &AddExpressionProps) -> HtmlResult {
})
}

#[derive(Properties, PartialEq, Clone, Debug)]
struct EditExpressionProps {
name: String,
value: DeltaQ,
selected: bool,
on_change: Callback<(String, Option<DeltaQ>)>,
}

#[function_component(EditExpression)]
fn edit_expression(props: &EditExpressionProps) -> HtmlResult {
let name = props.name.clone();
let on_change = props.on_change.clone();

let editing = use_state(|| false);
let buffer = use_state(|| props.value.to_string());
let result = use_state(|| "OK".to_owned());

let value = use_state(|| props.value.clone());
let on_submit = cloned!(name, value, on_change, editing; Callback::from(move |e: SubmitEvent| {
e.prevent_default();
editing.set(false);
on_change.emit((name.clone(), Some((*value).clone())));
}));
let on_input = cloned!(buffer, value, result; Callback::from(move |e: InputEvent| {
let v = e.target_unchecked_into::<HtmlInputElement>().value();
buffer.set(v.clone());
match v.parse::<DeltaQ>() {
Ok(dq) => {
value.set(dq);
result.set("OK".to_owned());
}
Err(e) => {
result.set(e);
}
}
}));

use_memo(
(props.value.clone(), props.selected),
cloned!(editing, value, buffer; move |x| {
editing.set(false);
value.set(x.0.clone());
buffer.set(x.0.to_string());
}
),
);

Ok(html! {
if *editing {
<div class={classes!("dq_edit")}>
<form onsubmit={on_submit}>
<input type="submit" value="update" />
<input type="text" value={(*buffer).clone()} oninput={on_input} />
</form>
<pre>{ &*result }</pre>
</div>
} else {
<span class={classes!("dq_show")} onclick={cloned!(editing; move |_| editing.set(true))}>{ value.to_string() }</span>
}
})
}

#[function_component(App)]
fn app() -> Html {
let waiting = html! { <p>{ "Waiting for DeltaQ..." }</p> };
Expand Down
2 changes: 1 addition & 1 deletion delta_q/src/bin/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ async fn main() -> io::Result<()> {
ctx.put("black".to_owned(), DeltaQ::BlackBox);
ctx.put(
"cdf".to_owned(),
DeltaQ::cdf(CDF::step(&[(0.1, 0.33), (0.2, 0.66), (0.4, 1.0)], 0.01, 300).unwrap()),
DeltaQ::cdf(CDF::step(&[(0.1, 0.33), (0.2, 0.66), (0.4, 1.0)]).unwrap()),
);
ctx.put(
"out".to_owned(),
Expand Down
Loading

0 comments on commit 088d46d

Please sign in to comment.