# Qi-circuit - A domain specific language to create signal flow graphs

**URL:** <https://racket.discourse.group/t/qi-circuit-a-domain-specific-language-to-create-signal-flow-graphs/2610>\
**Category:** Show & Tell\
**Tags:** qi, stream, dsl, circuit\
**Created:** [December 18, 2023, 7:13pm UTC](https://racket.discourse.group/t/qi-circuit-a-domain-specific-language-to-create-signal-flow-graphs/2610 "2023-12-18T19:13:52Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![chansey97](https://avatars.discourse-cdn.com/v4/letter/c/e480ec/32.png) [@chansey97](https://racket.discourse.group/u/chansey97)\
**Post date:** [December 18, 2023, 7:13pm UTC](https://racket.discourse.group/t/qi-circuit-a-domain-specific-language-to-create-signal-flow-graphs/2610/1 "2023-12-18T19:13:52Z")

</div>

Hello everyone,

I'm excited to share a package that I've recently developed, built on top of [Qi](https://docs.racket-lang.org/qi/index.html), called Qi-circuit: [https://github.com/chansey97/qi-circuit](https://github.com/chansey97/qi-circuit "https://github.com/chansey97/qi-circuit")

Qi-circuit a domain-specific language designed for creating stream circuits; in some literature, they are also referred to as signal flow graphs. The main aim of this package is to reveal the connection between Qi and signal flow graphs.

A quick example of calculating the Fibonacci sequence by Qi-circuit.

```scheme
#lang racket

(require qi)
(require "../qi-circuit-lib/circuit.rkt")

(define zero (stream-cons 0 zero))
(define one (~>> (zero) (c-reg 1)))

(define-flow sf
  (~>> (c-loop (~>> (== _ (c-reg 0))
                    (c-add +)
                    (c-loop (~>> (== _ (c-reg 0)) (c-add +) (-< _ _)))
                    (c-reg 0)
                    (-< _ _)))))

(define fib ((☯ sf) one)) 
(~>> (fib) (stream-take _ 20) stream->list)
;; '(0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181)

```

I have written a [tutorial](https://github.com/chansey97/qi-circuit) about it, including many examples, such as integral, factorial, fibonacci, catalan numbers, and ODE. Each example contains a step-by-step diagram reasoning to transform a general circuit to Qi-circuit.

Give it a try and let me know what you think about. Any insights, suggestions, and criticism are highly appreciated.

Thanks!

---

<div class="post-metadata">

**Author:** ![benknoble](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/benknoble/32/16_2.png) [@benknoble](https://racket.discourse.group/u/benknoble)\
**Post date:** [December 18, 2023, 7:27pm UTC](https://racket.discourse.group/t/qi-circuit-a-domain-specific-language-to-create-signal-flow-graphs/2610/2 "2023-12-18T19:27:21Z")

</div>

It seems to me that your definition of `sf` repeats the body a bit?

```scheme
(define-flow sf
  (~>> (c-loop (~>> (== _ (c-reg 0))
                    (c-add +)
                    (c-loop (~>> (== _ (c-reg 0)) (c-add +) (-< _ _)))
                    (c-reg 0)
                    (-< _ _)))))

```

I wonder if that was intentional, and if so, why?

---

<div class="post-metadata">

**Author:** ![chansey97](https://avatars.discourse-cdn.com/v4/letter/c/e480ec/32.png) [@chansey97](https://racket.discourse.group/u/chansey97)\
**Post date:** [December 18, 2023, 8:39pm UTC](https://racket.discourse.group/t/qi-circuit-a-domain-specific-language-to-create-signal-flow-graphs/2610/3 "2023-12-18T20:39:57Z")

</div>

Thanks for your feedback!

What do you mean by " `sf` repeats the body a bit?" ?

Note that the same stream function can have different circuits, but they are equivalent. For Fibonacci sequence, you might write a different circuit. The circuit in the example uses a general circuit for any rational stream.

More details, see [qi-circuit/qi-circuit-examples/fibonacci.md at main · chansey97/qi-circuit · GitHub](https://github.com/chansey97/qi-circuit/blob/main/qi-circuit-examples/fibonacci.md)

---

<div class="post-metadata">

**Author:** ![benknoble](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/benknoble/32/16_2.png) [@benknoble](https://racket.discourse.group/u/benknoble)\
**Post date:** [December 18, 2023, 9:43pm UTC](https://racket.discourse.group/t/qi-circuit-a-domain-specific-language-to-create-signal-flow-graphs/2610/4 "2023-12-18T21:43:20Z")

</div>

The pattern `(~>> (== _ (c-reg 0) (c-add +) (-< _ _))` looks nested  
within itself a bit, modulo an extra `(c-reg 0)` and placement of  
threading.

---

<div class="post-metadata">

**Author:** ![chansey97](https://avatars.discourse-cdn.com/v4/letter/c/e480ec/32.png) [@chansey97](https://racket.discourse.group/u/chansey97)\
**Post date:** [December 18, 2023, 9:52pm UTC](https://racket.discourse.group/t/qi-circuit-a-domain-specific-language-to-create-signal-flow-graphs/2610/5 "2023-12-18T21:52:58Z")

</div>

This is `c-loop` construct.

![image](https://global.discourse-cdn.com/free1/uploads/racket/original/2X/a/a78eee2af0df0e73fe41d4136fe339dab2a334d0.png)

The `(~>> (== _ (c-reg 0) (c-add +) (-< _ _))` is the `sf` in `c-loop` (blue dashed rectange), so it has two inputs and two outputs.

Maybe I misunderstood your question.

---

<div class="post-metadata">

**Author:** ![chansey97](https://avatars.discourse-cdn.com/v4/letter/c/e480ec/32.png) [@chansey97](https://racket.discourse.group/u/chansey97)\
**Post date:** [December 20, 2023, 6:48am UTC](https://racket.discourse.group/t/qi-circuit-a-domain-specific-language-to-create-signal-flow-graphs/2610/8 "2023-12-20T06:48:15Z")

</div>

I deleted the previous reply, which is a bit confusing; here is the updated answer.

For Fibonacci sequence, its generating function is F = \frac{X}{1 - X - X^2}.

We can derive the recursive equation F = X + F X + F X^2 from it.

One way to solve the recursive equation is to use stream algorithms, i.e.

```scheme
(define F (stream-cons 0 (stream-cons 1 (map + F (stream-rest F)))))
(~>> (F) (stream-take _ 20) stream->list)
;; '(0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181)

```

However, the recursive equation can also be represented as circuits.

For example,

 ![image-20231220140125664](https://global.discourse-cdn.com/free1/uploads/racket/original/2X/5/50e67f8996a0616e538cc13ffbf3298ed1c1962f.png)

It's obvious that it has two loops. However, there are many ways for solving the same recursive equation; this is just one of them. i.e. F = (X + F X) + F X^2.

As follows are other possible implementations:

F = (X + F X^2) + F X

 ![image-20231220140434941](https://global.discourse-cdn.com/free1/uploads/racket/original/2X/4/48dfc235be219845f25efa11a68b2dc34152a372.png)

F = (F X + F X^2) + X

 ![image-20231220135853380](https://global.discourse-cdn.com/free1/uploads/racket/original/2X/c/c7fd80478eea9f82aafad9d439a668445a85ea02.png)

F = (1 + F X + F) X

 ![image-20231220135548419](https://global.discourse-cdn.com/free1/uploads/racket/original/2X/a/ad2b80c51c91b6714963f38385effb9b741c1f53.png)

F = X + F (X + X^2)

 ![image-20231220115342196](https://global.discourse-cdn.com/free1/uploads/racket/original/2X/2/2819e2905b757c708aa0f7da93b48ac790844b4d.png)

Look the last one, which has only one loop. We can use the circuit as a template to create Qi-circuit.

 ![image-20231220115612955](https://global.discourse-cdn.com/free1/uploads/racket/original/2X/3/3ae675c944e5e18e83f252e87cd1d772ff25b552.png)

```scheme
(define fib
  (~>> (one)
       (c-reg 0)
       (c-loop (~>> (== _ (~>> (-< (c-reg 0) (~>> (c-reg 0) (c-reg 0))) (c-add +)))
                    (c-add +)
                    (-< _ _)))))

(~>> (fib) (stream-take _ 20) stream->list)
;; '(0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181)

```

This answer your question "why there are two loops".

* * *

BTW, in the latest Qi-circuit, I added a new form `c-loop-gen`.

![image-20231220133353173](https://global.discourse-cdn.com/free1/uploads/racket/original/2X/7/7a63e05f0d89c25c1a51420ea21079ba9ed81114.png)

It can output signals without any input. Now, there's no longer to use a dummy input (such as zero) and `>2` as was required previously.

The Fibonacci sequence can also be defined as follows:

F = (F + (FX + 1)) X

 ![image-20231220132518110](https://global.discourse-cdn.com/free1/uploads/racket/original/2X/7/7a045cfbe0f252bdb3307d436c9aad9c8d013336.png)

 ![image-20231220132553877](https://global.discourse-cdn.com/free1/uploads/racket/original/2X/1/19da087d868957676c3528f633502c347e02a895.png)

```scheme
(define fib
  (~>> ()
       (c-loop-gen (~>> (c-reg 0) (-< _ (c-reg 1)) (c-add +) (-< _ _)))
       (c-reg 0)
       ))

(~>> (fib) (stream-take _ 20) stream->list)
;; '(0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181)

```

This circuit seems more readable.

---

<div class="post-metadata">

**Author:** ![robby](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/robby/32/7_2.png) [@robby](https://racket.discourse.group/u/robby)\
**Post date:** [December 20, 2023, 4:49pm UTC](https://racket.discourse.group/t/qi-circuit-a-domain-specific-language-to-create-signal-flow-graphs/2610/9 "2023-12-20T16:49:29Z")

</div>

All the talk of circuits and streams reminds me of the Lustre programming language. Do you guys know it? Here's how to implement fib in it:

```scheme
node fib () returns (f: int);
let 
  f = 1 -> pre(f + (0 -> pre f));
tel

```

Here's a transcript of what running this in [lv6](https://gricad-gitlab.univ-grenoble-alpes.fr/verimag/synchrone/lustre-v6) looks like:

```scheme
$ lv6 -cc -2c -n fib fib.lus
W: fib_fib_loop.c has been generated.
W: fib_fib.h has been generated.
W: fib_fib.c has been generated.
+ '[' -z ']'
+ C_COMPILER=gcc
+ '[' -z ']'
+ MAIN_FILE=fib_fib_loop.c
+ gcc -o fib.exec lustre_consts.c fib_fib.c fib_fib_loop_io.c fib_fib_loop.c
fib_fib_loop.c:11:48: warning: passing arguments to 'fib_fib_ctx_new_ctx' without a prototype is deprecated in all versions of C and is not supported in C2x [-Wdeprecated-non-prototype]
    fib_fib_ctx_type* ctx = fib_fib_ctx_new_ctx(NULL);
                                               ^
1 warning generated.
sys call: 'sh fib.sh'

$ ./fib.exec 
#inputs 
#outputs "f":int
#step 1 
1
#step 2 
1
#step 3 
2
#step 4 
3
#step 5 
5
#step 6 
8
#step 7 
13
#step 8 
21
#step 9 
34
#step 10 
55
#step 11 
89
#step 12 
144

```

---

<div class="post-metadata">

**Author:** ![chansey97](https://avatars.discourse-cdn.com/v4/letter/c/e480ec/32.png) [@chansey97](https://racket.discourse.group/u/chansey97)\
**Post date:** [December 23, 2023, 9:26am UTC](https://racket.discourse.group/t/qi-circuit-a-domain-specific-language-to-create-signal-flow-graphs/2610/10 "2023-12-23T09:26:56Z")

</div>

Thank you for telling me the Lustre language. I read some articles online about Lustre, and it's very cool!

Inspired by [Lustre](https://homepage.cs.uiowa.edu/~tinelli/classes/181/Spring10/Notes/03-lustre.pdf) , I added two new forms to Qi-circuit.

![image-20231223151615811](https://global.discourse-cdn.com/free1/uploads/racket/original/2X/4/4874398ccbe2d92c8c25ff871d85637faac3d7b9.png)

![image-20231223150807313](https://global.discourse-cdn.com/free1/uploads/racket/original/2X/a/a9430736f741fd3436b1a9e2af951eb462bdcf00.png)

They correspond to `->` and `if then else` in Lustre.

Now we can create more interesting circuits.

For example, the bistable switch:

 ![image-20231223144005731](https://global.discourse-cdn.com/free1/uploads/racket/original/2X/0/0fa9c7a267494efcd6790fb4832dd528ac511d62.png)

```scheme
(define (bistable-switch on off)
  (~>> (on off)
       ▽
       (c-loop (~>> (== △ (~>> (-< (gen false) (c-reg 0)) c-->))
                    (c-switch (% 3> _)
                              [_ (~>> 2> NOT)]
                              [else 1>])
                    (-< _ _)
                    ))))

```

In Lustre, it would be write

```scheme
node Switch(on,off: bool) returns (s: bool);
let s = if(false -> pre s) then not off else on; tel

```

Qi-circuit seems more cumbersome than Lustre.

BTW, do you think Lustre is readable? or some one has to draw out the diagram for understanding? Qi-circuit at least maintains the shape of the circuit.

---

<div class="post-metadata">

**Author:** ![chansey97](https://avatars.discourse-cdn.com/v4/letter/c/e480ec/32.png) [@chansey97](https://racket.discourse.group/u/chansey97)\
**Post date:** [December 23, 2023, 9:27am UTC](https://racket.discourse.group/t/qi-circuit-a-domain-specific-language-to-create-signal-flow-graphs/2610/11 "2023-12-23T09:27:05Z")

</div>

Next task is adding Lustre's Clock to Qi-circuit.

But before that, I have to think about the issue of modularity. Currently Qi-circuit seems [not suitable for modularization (e.g. the ODE example)](https://github.com/chansey97/qi-circuit/blob/main/qi-circuit-examples/ode-1st.md) because Qi is strict in Racket:

> Note that this example also shows that you cannot treat `integral` as an independent component, because `(c-reg y0)` must be on the leftmost side of `sf` in the `c-loop` or `c-loop-gen`.

In other words, you often have to slide the `c-reg` to the left.

I haven't tried the same example with Arrow in Haskell, but it might be related. Naively use `#lang lazy` with `list` doesn't work at the moment, I have not explored deeply though.

Anyone has idea about it?

Thanks.

---

<div class="post-metadata">

**Author:** ![robby](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/robby/32/7_2.png) [@robby](https://racket.discourse.group/u/robby)\
**Post date:** [December 23, 2023, 2:28pm UTC](https://racket.discourse.group/t/qi-circuit-a-domain-specific-language-to-create-signal-flow-graphs/2610/12 "2023-12-23T14:28:35Z")

</div>

> [@chansey97](#):
>
> BTW, do you think Lustre is readable? or some one has to draw out the diagram for understanding? Qi-circuit at least maintains the shape of the circuit.

I know only very little about Lustre. It is a storied programming language, however, dating back to the 1980s and with a lot of very serious people using it for safety-critical things and studying its properties and making it more and more robust. There is an annual meeting called [Synchron](https://www.rtsys.informatik.uni-kiel.de/en/synchron-2023/) where many of the people who go work full-time on Lustre and related things. I don't know if it has an industrial community, tho. That's an academic community. I'm guessing that they would welcome a presentation on your work if you can manage a trip to Europe and are intersted in talking with them. (I don't know an online way to connect to that community, however.)
