• No results found

Circuit Arrows

In document Arrows for knowledge based circuits (Page 114-119)

We now sketch our circuit-specific Arrows using the technique of the previous section to specify them. This framework forces us to clearly distinguish amongst:

• values that flow along the wires;

• operations connected by these wires; and

• circuit generators that create networks of operations connected by wires.

As observed byErkök(2002, p9), a key intuition is to determine when effects are used: do they occur at “run time” – during the interpretation of the circuit – or while generating it? The former should be circuit Arrows, while the latter can be deferred to the general mechanisms of Haskell.

In our case, the basic effect is a unit-time delay operation, from which we can build the finite memories of actual circuits. We are also interested in other effects such as probes and non- determinism (§5.2.5), and later, knowledge (§6.1).

5.2.1 The

ArrowComb

class

We begin with the foundationalArrowCombclass:

typeB( ) :: ∗ falseA :: γ B( ) trueA :: γ B( ) andA :: (B( ),B( )) B( ) notA :: B( ) B( ) note :: String → (b c) → (b c) note_ = id

This is essentially the final definition of §5.1.2with the trivial addition of anotefunction that is useful for delineating subcircuits (such as in the netlist interpretation of §5.4.1). We use the

associated type(Chakravarty et al. 2005)B( ) instead of the type-class parameterbit, which has

the effect of making the type of bits a function of the Arrow ( ). This eliminates the ambiguity that would need to be resolved by the user at the cost of only allowing one type of bit per Arrow.

In practice we add many redundant combinational gates and define standard logical syntax for their command-combinator variants.

5.2.2 The

ArrowMux

class

A standard combinational circuit component is themultiplexer, which outputs one of its two data inputs depending on a separate selection input. We might hope to press the standard

ArrowChoiceclass into service as it would allow us to use thecaseandifcontrol constructs in

the Arrow notation. That class defines the (|||) operator that does to sum types what (∗∗∗) does to products:

classArrow( ) ⇒ ArrowChoice( )where ...

(|||) :: (α γ) → (β γ) → (Eitherα β γ)

Unfortunately this does not support reinterpretation as our interpretations may not be able to represent arbitrary polymorphic types. Moreover the semantics of synchronous circuits require us to clock (execute) both branches of the choice even if they are unselected, as they may update internal state. This would involve manufacturing a value of typeαorβnot provided by the input, which we cannot parametrically do. We might say thatArrowMuximplements a form of clock gating.

For these reasons we need to provide our own choice combinator:

classArrowComb( ) ⇒ ArrowMux( )αwhere

muxA :: (B( ), (α,α)) α

with the expectation thatmuxAoutputs the first value if the condition is true, and the second otherwise. We include the typeαin the head of the type class to support the generics of §5.3.

5.2.3 The

ArrowDelay

class

We need a primitive delay operation to support sequential circuits. As observed in §5.1.2the statically-initialised operation:

delayA :: α → (α α)

cannot be initialised at typeB( ) as we have no way of feeding the result of the constant Arrows to thedelayAfunction. (Recall that we can embed pure functions in Arrows, but not necessarily the other way around.) For this reason we ask for this operation:

classArrow( ) ⇒ ArrowDelay( )αwhere

delayA :: (α,α) α

The first value is yielded bydelayAin the first instant, and at later instantsdelayAyields the sec- ond value from the previous instant; the first argument is ignored at later times. The command combinator variant:

delayAC :: ArrowDelay( )α⇒ (γ α) → (γ α) → (γ α)

delayAC = liftAC2delayA

is essentially the followed-by, or initialised delay, operator->of Lustre (see §4.3.1andHalbwachs et al.(1991)). We again use an MPTC to support the generics of §5.3.

5.2.4 The

ArrowCombLoop

class

We expect our interpretations to provide instances of theArrowLoopclass defined byPaterson (2001):

classArrowLoop( )where

loop :: ((α,γ) (β,γ))→ (α β)

However as we discussed in §4.1, we can only expect these instances to work when the cycle includes a delay, in the classic sequential circuit tradition; this allows the use of the very conve- nientrecsyntax we discussed in §4.2.4. Thisloopcombinator can be considered a variant of the recursion combinator proposed for the original Lava (§4.2.4), or a generalisation of the original

µoperator ofµFP (§4.2.1). Intuitivelyloopfallowsf to recursively define and use a value of type

γ, but the effects off should only be done once.

To support combinationally-cyclic circuits we define theArrowCombLoopclass:

classArrow( ) ⇒ ArrowCombLoop( )γwhere

In contrast toloopwe cannot expectcombLoopto be uniformly polymorphic as we typically use Kleene iteration to find the fixed points of these cycles, and so need a (reified!) least element to start from. Similarly we cannot expectcombLoopto perform the effects of its argument once, and so we lose many of the properties ofloop. In particular we cannot move impure Arrows out of the scope ofcombLoop(Paterson’sTIGHTENINGrules), but the remainder involving pure Arrows should be satisfied:

EXTENSION loop(arrf)=arr(tracef)

SLIDING loop(f>>>arr(i d×k))=loop(arr(id×k)>>>f)

VANISHING loop(loopf)=loop(arr unassoc>>>f >>>arr assoc)

SUPERPOSING second(loopf)=loop(arr assoc>>>secondf>>>arr unassoc) where trace :: ((α,γ) → (β,γ))→ αβ tracef = λx.let(y,z) = f (x,z)iny assoc :: ((α,β),γ))→ (α, (β,γ)) assoc ∼(∼(x,y),z) = (x, (y,z)) unassoc :: (α, (β,γ))→ ((α,β),γ) unassoc ∼(x,∼(y,z)) = ((x,y),z) 5.2.5 Meta-circuits

When modelling scenarios it is often convenient to add primitives that are not realisable; for instance we wish to model coin flips and abstract from the decisions of the environment, and to examine the internal state of a circuit without changing its interface. The following classes provide these facilities.

Probes

It is not always desirable or easy to expose all the signals of interest at module interfaces; doing so may violate abstraction and complicate composition. We therefore we provideprobesthat give names to arbitrary collections of signals:

classArrow( ) ⇒ ArrowProbe( )αwhere

probeA :: ProbeID → (α α)

We expect the Arrow ( ) to record these probes in a global scope; we provide no means to access them from within a standard circuit but will use them extensively in concert with our machinery for knowledge-based circuits and model checking in the next chapter. While this approach is non-compositional, it is sufficient for our purposes to require that all labels used in a circuit be mutually distinct.

Non-determinism

Some behaviours of an environment or agent’s behaviour are non-deterministic; some are genuinely so, such as an agent flipping a coin, and others are simply underspecified. We provide a basic class for non-deterministic choice:

classArrow( ) ⇒ ArrowNonDet( )αwhere

nondetA :: (α,α) α

nondetFairA :: (α,α) α

From this we can easily define other constructs such as a non-deterministic bit:

nondetBitA = trueA‘nondetAC‘ falseA

We make very sparing use of fairness when it eases correctness assertions.

As such choices are represented by state variables, we also include anondetLatchACcombinator wherenondetLatchACpchooses a value that satisfiespin the first instant, and returns that forever more. UsingdelayAin this way would use twice as many state variables as necessary. SimilarlynondetChooseACpmakes a fresh choice every instant.

5.2.6 Two examples

Using the mechanisms defined above, we can describe the twisted ring counter of §4.1as follows:

trc :: (ArrowComb( ),ArrowDelay( )) ⇒ () (B( ),B( ),B( ))

trc = proc()→

do recx ← LdelayAC(falseA−≺())

LandACL orAC(returnA −≺x) (notA −≺y))M (notA −≺z))M M

y ← LdelayAC(falseA−≺()) (returnA −≺x)M

z ← LdelayAC(falseA −≺()) (returnA−≺z)M

returnA−≺(x,y,z)

We can use therecsyntax as all loops pass through a delay.

In contrast the cyclic circuitfgORgfof Figure4.4requires an explicit use ofcombLoop:

fgORgf :: (ArrowCombLoop( )α,ArrowMux( )α)

⇒ (α α) → (α α) → (B( ),α) α

f ‘fgORgf‘g =proc(choose,x) →

L combLoop(λgOut.

dofOutf <<<muxA−≺(choose, (x,gOut))

gOut0 g <<<muxA−≺(choose, (fOut,x))

out ← muxA−≺(choose, (gOut,fOut))

counter :: Signal Bool → [Signal Bool]

counterinc =sum : sums

where

(sum,carryOut) = halfAdd(inc,sum0)

sums =countercarryOut

sum0 = delay Falsesum

halfAdd :: (Signal Bool,Signal Bool)

→ (Signal Bool,Signal Bool)

halfAdd(a,b) = (sum,carry) where

sum = xora b

carry= and2a b

Figure 5.2: A counter with a bit width specified by its context.

We can also write it without the command combinator syntax:

f ‘fgORgf‘g = combLooparrow

where

arrow = proc((choose,x),gOut) →

dofOutf <<<muxA−≺(choose, (x,gOut))

gOut0 ← g <<<muxA−≺(choose, (fOut,x))

out ← muxA−≺(choose, (gOut0,fOut))

returnA−≺(out,gOut0)

In document Arrows for knowledge based circuits (Page 114-119)