eyeling 1.17.2 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HANDBOOK.md +162 -14
- package/examples/bmi.n3 +219 -0
- package/examples/output/bmi.n3 +20 -0
- package/examples/output/pn-junction-tunneling.n3 +23 -0
- package/examples/output/sudoku.n3 +42 -0
- package/examples/output/transistor-switch.n3 +24 -0
- package/examples/pn-junction-tunneling.n3 +227 -0
- package/examples/sudoku.n3 +257 -0
- package/examples/transistor-switch.n3 +299 -0
- package/eyeling.js +678 -2
- package/index.d.ts +21 -0
- package/index.js +13 -0
- package/lib/builtin-sudoku.js +465 -0
- package/lib/builtins.js +156 -0
- package/lib/cli.js +33 -2
- package/lib/engine.js +21 -0
- package/package.json +1 -1
- package/test/api.test.js +29 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# transistor-switch.n3
|
|
3
|
+
#
|
|
4
|
+
# - supply voltage = 5000 mV
|
|
5
|
+
# - low input = 0 mV
|
|
6
|
+
# - high input = 5000 mV
|
|
7
|
+
# - base-emitter turn-on = 700 mV
|
|
8
|
+
# - collector-emitter sat = 200 mV
|
|
9
|
+
# - base resistor = 10000 ohms
|
|
10
|
+
# - load resistor = 1000 ohms
|
|
11
|
+
# - transistor beta proxy = 100
|
|
12
|
+
#
|
|
13
|
+
# We model an NPN low-side switch with exact millivolt and microamp arithmetic.
|
|
14
|
+
# If Vin <= Vbe,on the transistor stays in cutoff and the collector branch is
|
|
15
|
+
# OFF. If Vin > Vbe,on then Ib = (Vin - Vbe,on) * 1000 / Rb, the gain-limited
|
|
16
|
+
# collector current is beta * Ib, the load-limited current is
|
|
17
|
+
# (Vcc - Vce,sat) * 1000 / Rl, and the actual collector current is the smaller
|
|
18
|
+
# of those two limits.
|
|
19
|
+
# =============================================================================
|
|
20
|
+
|
|
21
|
+
@prefix : <http://example.org/transistor-switch#> .
|
|
22
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
23
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#> .
|
|
24
|
+
@prefix list: <http://www.w3.org/2000/10/swap/list#> .
|
|
25
|
+
@prefix string: <http://www.w3.org/2000/10/swap/string#> .
|
|
26
|
+
|
|
27
|
+
# -----
|
|
28
|
+
# Facts
|
|
29
|
+
# -----
|
|
30
|
+
|
|
31
|
+
:case :name "transistor-switch" .
|
|
32
|
+
:case :supplyMv 5000 .
|
|
33
|
+
:case :vbeOnMv 700 .
|
|
34
|
+
:case :vceSatMv 200 .
|
|
35
|
+
:case :baseResistanceOhms 10000 .
|
|
36
|
+
:case :loadResistanceOhms 1000 .
|
|
37
|
+
:case :beta 100 .
|
|
38
|
+
|
|
39
|
+
:low :inputLabel "low input" ; :inputMv 0 .
|
|
40
|
+
:high :inputLabel "high input" ; :inputMv 5000 .
|
|
41
|
+
|
|
42
|
+
:mvfmt-0 :mvValue 0 ; :text "0.00 V" .
|
|
43
|
+
:mvfmt-200 :mvValue 200 ; :text "0.20 V" .
|
|
44
|
+
:mvfmt-4800 :mvValue 4800 ; :text "4.80 V" .
|
|
45
|
+
:mvfmt-5000 :mvValue 5000 ; :text "5.00 V" .
|
|
46
|
+
|
|
47
|
+
:uafmt-0 :uaValue 0 ; :text "0.00 mA" .
|
|
48
|
+
:uafmt-430 :uaValue 430 ; :text "0.43 mA" .
|
|
49
|
+
:uafmt-4800 :uaValue 4800 ; :text "4.80 mA" .
|
|
50
|
+
:uafmt-43000 :uaValue 43000 ; :text "43.00 mA" .
|
|
51
|
+
|
|
52
|
+
# -----
|
|
53
|
+
# Logic
|
|
54
|
+
# -----
|
|
55
|
+
|
|
56
|
+
# Low-input branch: below turn-on, the transistor stays in cutoff.
|
|
57
|
+
{ ?state list:in (:low :high) .
|
|
58
|
+
?state :inputMv ?vin .
|
|
59
|
+
:case :vbeOnMv ?vbe .
|
|
60
|
+
?vin math:notGreaterThan ?vbe .
|
|
61
|
+
:case :supplyMv ?vcc .
|
|
62
|
+
}
|
|
63
|
+
=> { ?state :baseCurrentUa 0 .
|
|
64
|
+
?state :collectorGainLimitUa 0 .
|
|
65
|
+
?state :collectorLoadLimitUa 0 .
|
|
66
|
+
?state :collectorCurrentUa 0 .
|
|
67
|
+
?state :loadVoltageMv 0 .
|
|
68
|
+
?state :collectorEmitterVoltageMv ?vcc .
|
|
69
|
+
?state :cutoff true .
|
|
70
|
+
?state :saturation false .
|
|
71
|
+
?state :stateLabel "cutoff / OFF" . } .
|
|
72
|
+
|
|
73
|
+
# Forward-biased branch: derive base current and the two collector limits.
|
|
74
|
+
{ ?state list:in (:low :high) .
|
|
75
|
+
?state :inputMv ?vin .
|
|
76
|
+
:case :vbeOnMv ?vbe .
|
|
77
|
+
?vin math:greaterThan ?vbe .
|
|
78
|
+
:case :baseResistanceOhms ?rb .
|
|
79
|
+
( ?vin ?vbe ) math:difference ?overdriveMv .
|
|
80
|
+
( ?overdriveMv 1000 ) math:product ?baseNumerator .
|
|
81
|
+
( ?baseNumerator ?rb ) math:integerQuotient ?baseCurrentUa .
|
|
82
|
+
:case :beta ?beta .
|
|
83
|
+
( ?baseCurrentUa ?beta ) math:product ?collectorGainLimitUa .
|
|
84
|
+
:case :supplyMv ?vcc .
|
|
85
|
+
:case :vceSatMv ?vceSat .
|
|
86
|
+
:case :loadResistanceOhms ?rl .
|
|
87
|
+
( ?vcc ?vceSat ) math:difference ?loadSpanMv .
|
|
88
|
+
( ?loadSpanMv 1000 ) math:product ?loadNumerator .
|
|
89
|
+
( ?loadNumerator ?rl ) math:integerQuotient ?collectorLoadLimitUa .
|
|
90
|
+
}
|
|
91
|
+
=> { ?state :baseCurrentUa ?baseCurrentUa .
|
|
92
|
+
?state :collectorGainLimitUa ?collectorGainLimitUa .
|
|
93
|
+
?state :collectorLoadLimitUa ?collectorLoadLimitUa .
|
|
94
|
+
?state :cutoff false . } .
|
|
95
|
+
|
|
96
|
+
# Saturated ON branch: gain limit is large enough to reach the load limit.
|
|
97
|
+
# Only applies after the forward-biased branch has established cutoff=false.
|
|
98
|
+
{ ?state :cutoff false .
|
|
99
|
+
?state :collectorGainLimitUa ?gainLimit .
|
|
100
|
+
?state :collectorLoadLimitUa ?loadLimit .
|
|
101
|
+
?gainLimit math:notLessThan ?loadLimit .
|
|
102
|
+
:case :loadResistanceOhms ?rl .
|
|
103
|
+
( ?loadLimit ?rl ) math:product ?loadVoltageNumerator .
|
|
104
|
+
( ?loadVoltageNumerator 1000 ) math:integerQuotient ?loadVoltageMv .
|
|
105
|
+
:case :vceSatMv ?vceSat .
|
|
106
|
+
}
|
|
107
|
+
=> { ?state :collectorCurrentUa ?loadLimit .
|
|
108
|
+
?state :loadVoltageMv ?loadVoltageMv .
|
|
109
|
+
?state :collectorEmitterVoltageMv ?vceSat .
|
|
110
|
+
?state :saturation true .
|
|
111
|
+
?state :stateLabel "saturation / ON" . } .
|
|
112
|
+
|
|
113
|
+
# Active branch: included for completeness, although the sampled high input
|
|
114
|
+
# reaches saturation in this toy case. It also only applies after cutoff=false.
|
|
115
|
+
{ ?state :cutoff false .
|
|
116
|
+
?state :collectorGainLimitUa ?gainLimit .
|
|
117
|
+
?state :collectorLoadLimitUa ?loadLimit .
|
|
118
|
+
?gainLimit math:lessThan ?loadLimit .
|
|
119
|
+
:case :loadResistanceOhms ?rl .
|
|
120
|
+
( ?gainLimit ?rl ) math:product ?loadVoltageNumerator .
|
|
121
|
+
( ?loadVoltageNumerator 1000 ) math:integerQuotient ?loadVoltageMv .
|
|
122
|
+
:case :supplyMv ?vcc .
|
|
123
|
+
( ?vcc ?loadVoltageMv ) math:difference ?vceMv .
|
|
124
|
+
}
|
|
125
|
+
=> { ?state :collectorCurrentUa ?gainLimit .
|
|
126
|
+
?state :loadVoltageMv ?loadVoltageMv .
|
|
127
|
+
?state :collectorEmitterVoltageMv ?vceMv .
|
|
128
|
+
?state :saturation false .
|
|
129
|
+
?state :stateLabel "active / linear" . } .
|
|
130
|
+
|
|
131
|
+
# Derived report-level checks.
|
|
132
|
+
{ :low :cutoff true .
|
|
133
|
+
:low :baseCurrentUa 0 .
|
|
134
|
+
:low :collectorCurrentUa 0 .
|
|
135
|
+
:low :loadVoltageMv 0 .
|
|
136
|
+
:case :supplyMv ?vcc .
|
|
137
|
+
:low :collectorEmitterVoltageMv ?vcc .
|
|
138
|
+
}
|
|
139
|
+
=> { :case :lowInputStaysInCutoff true . } .
|
|
140
|
+
|
|
141
|
+
{ :high :saturation true .
|
|
142
|
+
:case :vceSatMv ?vceSat .
|
|
143
|
+
:high :collectorEmitterVoltageMv ?vceSat .
|
|
144
|
+
}
|
|
145
|
+
=> { :case :highInputReachesSaturation true . } .
|
|
146
|
+
|
|
147
|
+
{ :low :cutoff true .
|
|
148
|
+
:high :saturation true .
|
|
149
|
+
}
|
|
150
|
+
=> { :case :switchesCleanly true . } .
|
|
151
|
+
|
|
152
|
+
{ :high :collectorCurrentUa ?ic .
|
|
153
|
+
:high :collectorLoadLimitUa ?ic .
|
|
154
|
+
:high :collectorGainLimitUa ?gain .
|
|
155
|
+
?gain math:greaterThan ?ic .
|
|
156
|
+
}
|
|
157
|
+
=> { :case :onStateIsLoadLimited true . } .
|
|
158
|
+
|
|
159
|
+
{ :high :collectorCurrentUa ?ic .
|
|
160
|
+
:case :loadResistanceOhms ?rl .
|
|
161
|
+
( ?ic ?rl ) math:product ?loadVoltageNumerator .
|
|
162
|
+
( ?loadVoltageNumerator 1000 ) math:integerQuotient ?expectedLoadVoltageMv .
|
|
163
|
+
:high :loadVoltageMv ?expectedLoadVoltageMv .
|
|
164
|
+
}
|
|
165
|
+
=> { :case :loadVoltageMatchesResistorDrop true . } .
|
|
166
|
+
|
|
167
|
+
# State summary rendering.
|
|
168
|
+
{ ?state list:in (:low :high) .
|
|
169
|
+
?state :inputMv ?vin .
|
|
170
|
+
?mvVin :mvValue ?vin ; :text ?vinText .
|
|
171
|
+
?state :baseCurrentUa ?ib .
|
|
172
|
+
?uaIb :uaValue ?ib ; :text ?ibText .
|
|
173
|
+
?state :collectorCurrentUa ?ic .
|
|
174
|
+
?uaIc :uaValue ?ic ; :text ?icText .
|
|
175
|
+
?state :collectorEmitterVoltageMv ?vce .
|
|
176
|
+
?mvVce :mvValue ?vce ; :text ?vceText .
|
|
177
|
+
?state :stateLabel ?label .
|
|
178
|
+
( "Vin=%s -> Ib=%s, Ic=%s, Vce=%s, state=%s\n" ?vinText ?ibText ?icText ?vceText ?label ) string:format ?summary .
|
|
179
|
+
}
|
|
180
|
+
=> { ?state :summaryLine ?summary . } .
|
|
181
|
+
|
|
182
|
+
# -----------------------------------------------
|
|
183
|
+
# Presentation (ARC: Answer • Reason Why • Check)
|
|
184
|
+
# -----------------------------------------------
|
|
185
|
+
|
|
186
|
+
# Answer
|
|
187
|
+
{ :low :stateLabel ?lowLabel .
|
|
188
|
+
:high :stateLabel ?highLabel .
|
|
189
|
+
:high :collectorCurrentUa ?onCurrentUa .
|
|
190
|
+
?uaOn :uaValue ?onCurrentUa ; :text ?onCurrentText .
|
|
191
|
+
( "low input state : %s\n" ?lowLabel ) string:format ?lowStateLine .
|
|
192
|
+
( "high input state : %s\n" ?highLabel ) string:format ?highStateLine .
|
|
193
|
+
( "on-state load current : %s\n\n" ?onCurrentText ) string:format ?onCurrentLine .
|
|
194
|
+
}
|
|
195
|
+
=> {
|
|
196
|
+
:01-answer-1 log:outputString "=== Answer ===\n" .
|
|
197
|
+
:01-answer-2 log:outputString "In this toy transistor-switch model, a low input leaves the transistor in cutoff (OFF) and a high input drives it into saturation (ON), so the load behaves like an on/off branch rather than a linear amplifier.\n" .
|
|
198
|
+
:01-answer-3 log:outputString "case : transistor-switch\n" .
|
|
199
|
+
:01-answer-4 log:outputString ?lowStateLine .
|
|
200
|
+
:01-answer-5 log:outputString ?highStateLine .
|
|
201
|
+
:01-answer-6 log:outputString ?onCurrentLine .
|
|
202
|
+
} .
|
|
203
|
+
|
|
204
|
+
# Reason Why
|
|
205
|
+
{ :case :supplyMv ?supplyMv .
|
|
206
|
+
?mvSupply :mvValue ?supplyMv ; :text ?supplyText .
|
|
207
|
+
:case :baseResistanceOhms ?rb .
|
|
208
|
+
:case :loadResistanceOhms ?rl .
|
|
209
|
+
:case :beta ?beta .
|
|
210
|
+
:low :summaryLine ?lowSummary .
|
|
211
|
+
:high :summaryLine ?highSummary .
|
|
212
|
+
:high :collectorGainLimitUa ?gainLimitUa .
|
|
213
|
+
?uaGain :uaValue ?gainLimitUa ; :text ?gainLimitText .
|
|
214
|
+
:high :collectorLoadLimitUa ?loadLimitUa .
|
|
215
|
+
?uaLoad :uaValue ?loadLimitUa ; :text ?loadLimitText .
|
|
216
|
+
( "supply voltage : %s\n" ?supplyText ) string:format ?supplyLine .
|
|
217
|
+
( "base resistor : %s ohms\n" ?rb ) string:format ?rbLine .
|
|
218
|
+
( "load resistor : %s ohms\n" ?rl ) string:format ?rlLine .
|
|
219
|
+
( "transistor beta proxy : %s\n" ?beta ) string:format ?betaLine .
|
|
220
|
+
( "low input : %s" ?lowSummary ) string:format ?lowLine .
|
|
221
|
+
( "high input : %s" ?highSummary ) string:format ?highLine .
|
|
222
|
+
( "high-input gain limit : %s\n" ?gainLimitText ) string:format ?gainLine .
|
|
223
|
+
( "high-input load limit : %s\n" ?loadLimitText ) string:format ?loadLine .
|
|
224
|
+
}
|
|
225
|
+
=> {
|
|
226
|
+
:02-why-01 log:outputString "=== Reason Why ===\n" .
|
|
227
|
+
:02-why-02 log:outputString "We model an NPN low-side switch with exact millivolt and microamp arithmetic. The base current comes from (Vin - Vbe,on)/Rb when the base-emitter junction is forward biased, and the collector current is the smaller of beta * Ib and the load-limited current (Vcc - Vce,sat)/Rl.\n" .
|
|
228
|
+
:02-why-03 log:outputString ?supplyLine .
|
|
229
|
+
:02-why-04 log:outputString ?rbLine .
|
|
230
|
+
:02-why-05 log:outputString ?rlLine .
|
|
231
|
+
:02-why-06 log:outputString ?betaLine .
|
|
232
|
+
:02-why-07 log:outputString ?lowLine .
|
|
233
|
+
:02-why-08 log:outputString ?highLine .
|
|
234
|
+
:02-why-09 log:outputString ?gainLine .
|
|
235
|
+
:02-why-10 log:outputString ?loadLine .
|
|
236
|
+
:02-why-99 log:outputString "\n" .
|
|
237
|
+
} .
|
|
238
|
+
|
|
239
|
+
# Check
|
|
240
|
+
{ :case :lowInputStaysInCutoff true .
|
|
241
|
+
:case :highInputReachesSaturation true .
|
|
242
|
+
:case :switchesCleanly true .
|
|
243
|
+
:case :onStateIsLoadLimited true .
|
|
244
|
+
:case :loadVoltageMatchesResistorDrop true .
|
|
245
|
+
}
|
|
246
|
+
=> {
|
|
247
|
+
:03-check-1 log:outputString "=== Check ===\n" .
|
|
248
|
+
:03-check-2 log:outputString "low input stays in cutoff : yes\n" .
|
|
249
|
+
:03-check-3 log:outputString "high input reaches saturation : yes\n" .
|
|
250
|
+
:03-check-4 log:outputString "switching states differ : yes\n" .
|
|
251
|
+
:03-check-5 log:outputString "on-state current is load-limited : yes\n" .
|
|
252
|
+
:03-check-6 log:outputString "load voltage matches resistor drop : yes\n" .
|
|
253
|
+
} .
|
|
254
|
+
|
|
255
|
+
# ------------------
|
|
256
|
+
# Verification fuses
|
|
257
|
+
# ------------------
|
|
258
|
+
|
|
259
|
+
# If any of the key invariants fail, stop the run loudly.
|
|
260
|
+
|
|
261
|
+
{ :low :baseCurrentUa ?ib .
|
|
262
|
+
?ib math:notEqualTo 0 .
|
|
263
|
+
} => false .
|
|
264
|
+
|
|
265
|
+
{ :low :collectorCurrentUa ?ic .
|
|
266
|
+
?ic math:notEqualTo 0 .
|
|
267
|
+
} => false .
|
|
268
|
+
|
|
269
|
+
{ :low :loadVoltageMv ?loadVoltageMv .
|
|
270
|
+
?loadVoltageMv math:notEqualTo 0 .
|
|
271
|
+
} => false .
|
|
272
|
+
|
|
273
|
+
{ :low :collectorEmitterVoltageMv ?lowVce .
|
|
274
|
+
:case :supplyMv ?vcc .
|
|
275
|
+
?lowVce math:notEqualTo ?vcc .
|
|
276
|
+
} => false .
|
|
277
|
+
|
|
278
|
+
{ :high :collectorEmitterVoltageMv ?highVce .
|
|
279
|
+
:case :vceSatMv ?vceSat .
|
|
280
|
+
?highVce math:notEqualTo ?vceSat .
|
|
281
|
+
} => false .
|
|
282
|
+
|
|
283
|
+
{ :high :collectorCurrentUa ?ic .
|
|
284
|
+
:high :collectorLoadLimitUa ?loadLimit .
|
|
285
|
+
?ic math:notEqualTo ?loadLimit .
|
|
286
|
+
} => false .
|
|
287
|
+
|
|
288
|
+
{ :high :collectorGainLimitUa ?gain .
|
|
289
|
+
:high :collectorLoadLimitUa ?loadLimit .
|
|
290
|
+
?gain math:notGreaterThan ?loadLimit .
|
|
291
|
+
} => false .
|
|
292
|
+
|
|
293
|
+
{ :high :collectorCurrentUa ?ic .
|
|
294
|
+
:case :loadResistanceOhms ?rl .
|
|
295
|
+
( ?ic ?rl ) math:product ?loadVoltageNumerator .
|
|
296
|
+
( ?loadVoltageNumerator 1000 ) math:integerQuotient ?expectedLoadVoltageMv .
|
|
297
|
+
:high :loadVoltageMv ?actualLoadVoltageMv .
|
|
298
|
+
?expectedLoadVoltageMv math:notEqualTo ?actualLoadVoltageMv .
|
|
299
|
+
} => false .
|