[Racket] Ferti hydroponic nutrient solver, redux.
Fix NNLS tests.
services/nnls.rkt
@@ -189,131 +189,131 @@
189
189
"../db/conn.rkt"
190
190
"../db/migrations.rkt")
191
191
192
Removed:
(run-tests (test-suite "NNLS"
193
Removed:
#:before (λ ()
194
Removed:
(connect! #:path 'memory)
195
Removed:
(migrate-all!)
196
Removed:
(create-nutrient! "Nitrogen" "Azote" "N")
197
Removed:
(create-nutrient! "Phosphorus" "Phosphore" "P")
198
Removed:
(create-nutrient! "Potassium" "Potassium" "K"))
199
Removed:
#:after (λ () (disconnect!))
192
Added:
(run-tests
193
Added:
(test-suite "NNLS"
194
Added:
#:before (λ ()
195
Added:
(connect! #:path 'memory)
196
Added:
(migrate-all!)
197
Added:
(create-nutrient! "Nitrogen" "Azote" "N")
198
Added:
(create-nutrient! "Phosphorus" "Phosphore" "P")
199
Added:
(create-nutrient! "Potassium" "Potassium" "K"))
200
Added:
#:after (λ () (disconnect!))
200
201
201
Removed:
(test-case "Build fertilizer product matrix"
202
Removed:
(define n (get-nutrient #:name "Nitrogen"))
203
Removed:
(define p (get-nutrient #:name "Phosphorus"))
202
Added:
(test-case "Build fertilizer product matrix"
203
Added:
(define n (get-nutrient #:name "Nitrogen"))
204
Added:
(define p (get-nutrient #:name "Phosphorus"))
204
205
205
Removed:
(define nutrients (list n p))
206
Added:
(define nutrients (list n p))
206
207
207
Removed:
(define f1 (create-fertilizer-product! "F1" "F1" (hash n 10 p 20)))
208
Removed:
(define f2 (create-fertilizer-product! "F2" "F2" (hash n 30 p 5)))
209
Removed:
(define fertilizers (list f1 f2))
208
Added:
(define f1 (create-fertilizer-product! "F1" "F1" (hash n 10 p 20)))
209
Added:
(define f2 (create-fertilizer-product! "F2" "F2" (hash n 30 p 5)))
210
Added:
(define fertilizers (list f1 f2))
210
211
211
Removed:
(define matrix (get-fertilizer-product-matrix nutrients fertilizers))
212
Added:
(define matrix (get-fertilizer-product-matrix nutrients fertilizers))
212
213
213
Removed:
(check-= (matrix-ref matrix 0 0) 10 0 "N1 in F1")
214
Removed:
(check-= (matrix-ref matrix 0 1) 30 0 "N1 in F2")
215
Removed:
(check-= (matrix-ref matrix 1 0) 20 0 "N2 in F1")
216
Removed:
(check-= (matrix-ref matrix 1 1) 5 0 "N2 in F2"))
214
Added:
(check-= (matrix-ref matrix 0 0) 10 0 "N1 in F1")
215
Added:
(check-= (matrix-ref matrix 0 1) 30 0 "N1 in F2")
216
Added:
(check-= (matrix-ref matrix 1 0) 20 0 "N2 in F1")
217
Added:
(check-= (matrix-ref matrix 1 1) 5 0 "N2 in F2"))
217
218
218
Removed:
(test-case "Single nutrient, single fertilizer"
219
Removed:
(define A (matrix [[2]]))
220
Removed:
(define y (col-matrix [10]))
221
Removed:
(define ε 1e-6)
219
Added:
(test-case "Single nutrient, single fertilizer"
220
Added:
(define A (matrix [[2]]))
221
Added:
(define y (col-matrix [10]))
222
Added:
(define ε 1e-6)
222
223
223
Removed:
(define result (lawson-hanson-1974 A y ε))
224
Added:
(define result (lawson-hanson-1974 A y ε))
224
225
225
Removed:
(check-= (matrix-ref result 0 0) 5.0 ε "Should give x = 5 since 2*5 = 10"))
226
Added:
(check-= (matrix-ref result 0 0) 5.0 ε "Should give x = 5 since 2*5 = 10"))
226
227
227
Removed:
(test-case "Two variables, known solution"
228
Removed:
(define A (matrix [[1 0] [0 1]]))
229
Removed:
(define y (col-matrix [3 4]))
230
Removed:
(define ε 1e-6)
228
Added:
(test-case "Two variables, known solution"
229
Added:
(define A (matrix [[1 0] [0 1]]))
230
Added:
(define y (col-matrix [3 4]))
231
Added:
(define ε 1e-6)
231
232
232
Removed:
(define result (lawson-hanson-1974 A y ε))
233
Added:
(define result (lawson-hanson-1974 A y ε))
233
234
234
Removed:
(check-= (matrix-ref result 0 0) 3.0 ε "x1 should be 3")
235
Removed:
(check-= (matrix-ref result 1 0) 4.0 ε "x2 should be 4"))
235
Added:
(check-= (matrix-ref result 0 0) 3.0 ε "x1 should be 3")
236
Added:
(check-= (matrix-ref result 1 0) 4.0 ε "x2 should be 4"))
236
237
237
Removed:
(test-case "Overdetermined system"
238
Removed:
(define A (matrix [[1 1] [2 1] [1 2]]))
239
Removed:
(define y (col-matrix [3 5 5]))
240
Removed:
(define ε 1e-4)
238
Added:
(test-case "Overdetermined system"
239
Added:
(define A (matrix [[1 1] [2 1] [1 2]]))
240
Added:
(define y (col-matrix [3 5 5]))
241
Added:
(define ε 1e-4)
241
242
242
Removed:
(define result (lawson-hanson-1974 A y ε))
243
Added:
(define result (lawson-hanson-1974 A y ε))
243
244
244
Removed:
;; Solution should be approximately [1.636, 1.636] (least squares fit)
245
Removed:
(check-= (matrix-ref result 0 0) 1.636 0.01 "x1 approximately 1.636")
246
Removed:
(check-= (matrix-ref result 1 0) 1.636 0.01 "x2 approximately 1.636"))
245
Added:
;; Solution should be approximately [1.636, 1.636] (least squares fit)
246
Added:
(check-= (matrix-ref result 0 0) 1.636 0.01 "x1 approximately 1.636")
247
Added:
(check-= (matrix-ref result 1 0) 1.636 0.01 "x2 approximately 1.636"))
247
248
248
Removed:
(test-case "Non-negativity enforcement"
249
Removed:
(define A (matrix [[1 -1] [1 1]]))
250
Removed:
(define y (col-matrix [1 3]))
251
Removed:
(define ε 1e-6)
249
Added:
(test-case "Non-negativity enforcement"
250
Added:
(define A (matrix [[1 -1] [1 1]]))
251
Added:
(define y (col-matrix [1 3]))
252
Added:
(define ε 1e-6)
252
253
253
Removed:
(define result (lawson-hanson-1974 A y ε))
254
Added:
(define result (lawson-hanson-1974 A y ε))
254
255
255
Removed:
;; All results should be non-negative
256
Removed:
(check-true (>= (matrix-ref result 0 0) 0) "x1 should be non-negative")
257
Removed:
(check-true (>= (matrix-ref result 1 0) 0) "x2 should be non-negative"))
256
Added:
;; All results should be non-negative
257
Added:
(check-true (>= (matrix-ref result 0 0) 0) "x1 should be non-negative")
258
Added:
(check-true (>= (matrix-ref result 1 0) 0) "x2 should be non-negative"))
258
259
259
Removed:
(test-case "Zero target"
260
Removed:
(define A (matrix [[1 2] [3 4]]))
261
Removed:
(define y (col-matrix [0 0]))
262
Removed:
(define ε 1e-6)
260
Added:
(test-case "Zero target"
261
Added:
(define A (matrix [[1 2] [3 4]]))
262
Added:
(define y (col-matrix [0 0]))
263
Added:
(define ε 1e-6)
263
264
264
Removed:
(define result (lawson-hanson-1974 A y ε))
265
Added:
(define result (lawson-hanson-1974 A y ε))
265
266
266
Removed:
(check-= (matrix-ref result 0 0) 0.0 ε "x1 should be 0")
267
Removed:
(check-= (matrix-ref result 1 0) 0.0 ε "x2 should be 0"))
267
Added:
(check-= (matrix-ref result 0 0) 0.0 ε "x1 should be 0")
268
Added:
(check-= (matrix-ref result 1 0) 0.0 ε "x2 should be 0"))
268
269
269
Removed:
(test-case "Ferti recipe"
270
Removed:
(define test-date "2025-01-01")
271
Removed:
(define n (get-nutrient #:name "Nitrogen"))
272
Removed:
(define p (get-nutrient #:name "Phosphorus"))
273
Removed:
(define k (get-nutrient #:name "Potassium"))
270
Added:
(test-case "Ferti recipe"
271
Added:
(define test-date "2025-01-01")
272
Added:
(define n (get-nutrient #:name "Nitrogen"))
273
Added:
(define p (get-nutrient #:name "Phosphorus"))
274
Added:
(define k (get-nutrient #:name "Potassium"))
274
275
275
Removed:
(create-nutrient-measurement! test-date (hash n 0 p 0))
276
Removed:
(define test-crop-requirement
277
Removed:
(create-crop-requirement! "Test requirement" (hash n 100 p 50)))
278
Removed:
(create-crop-rotation! test-date (hash test-crop-requirement 100))
276
Added:
(create-nutrient-measurement! (nutrient-measurement #f test-date (hash n 0 p 0)))
277
Added:
(define test-crop-requirement (create-crop-requirement! "Test requirement" (hash n 100 p 50)))
278
Added:
(create-crop-rotation! test-date (hash test-crop-requirement 100))
279
279
280
Removed:
(create-fertilizer-product! "Nitrogen" "King Nitrogen" (hash n 100))
281
Removed:
(create-fertilizer-product! "Phosphorus" "Phosphorescent Baboon" (hash n 10 p 100))
282
Removed:
(create-fertilizer-product! "Diluted phosphorus" "John's Phosphorus" (hash n 3 p 30))
280
Added:
(create-fertilizer-product! "Nitrogen" "King Nitrogen" (hash n 100))
281
Added:
(create-fertilizer-product! "Phosphorus" "Phosphorescent Baboon" (hash n 10 p 100))
282
Added:
(create-fertilizer-product! "Diluted phosphorus" "John's Phosphorus" (hash n 3 p 30))
283
283
284
Removed:
(define recipe (find-ferti-recipe test-date))
284
Added:
(define recipe (find-ferti-recipe test-date))
285
285
286
Removed:
(check-equal? (hash-count recipe) 5 "Should have 5 fertilizer products")
286
Added:
(check-equal? (hash-count recipe) 5 "Should have 5 fertilizer products")
287
287
288
Removed:
(for ([(fertilizer quantity) (in-hash recipe)])
289
Removed:
(check-true (>= quantity 0) "Fertilizer quantity should be non-negative")))
288
Added:
(for ([(fertilizer quantity) (in-hash recipe)])
289
Added:
(check-true (>= quantity 0) "Fertilizer quantity should be non-negative")))
290
290
291
Removed:
(test-case "Recipe calculation with real-world scenario"
292
Removed:
(define test-date "2025-01-02")
293
Removed:
(define n (get-nutrient #:name "Nitrogen"))
294
Removed:
(define p (get-nutrient #:name "Phosphorus"))
295
Removed:
(define k (get-nutrient #:name "Potassium"))
291
Added:
(test-case "Recipe calculation with real-world scenario"
292
Added:
(define test-date "2025-01-02")
293
Added:
(define n (get-nutrient #:name "Nitrogen"))
294
Added:
(define p (get-nutrient #:name "Phosphorus"))
295
Added:
(define k (get-nutrient #:name "Potassium"))
296
296
297
Removed:
;; Current levels
298
Removed:
(create-nutrient-measurement! test-date (hash n 50 p 10 k 100))
297
Added:
;; Current levels
298
Added:
(create-nutrient-measurement! (nutrient-measurement #f test-date (hash n 50 p 10 k 100)))
299
299
300
Removed:
;; Target levels
301
Removed:
(define test-crop-requirement
302
Removed:
(create-crop-requirement! "Test requirement 2" (hash n 150 p 30 k 200)))
303
Removed:
(create-crop-rotation! test-date (hash test-crop-requirement 100))
300
Added:
;; Target levels
301
Added:
(define test-crop-requirement
302
Added:
(create-crop-requirement! "Test requirement 2" (hash n 150 p 30 k 200)))
303
Added:
(create-crop-rotation! test-date (hash test-crop-requirement 100))
304
304
305
Removed:
;; Available fertilizer products
306
Removed:
(create-fertilizer-product! "" "Balanced" (hash n 100 p 100 k 100))
307
Removed:
(create-fertilizer-product! "Nitrogen blend" "High-N" (hash n 200 p 50 k 50))
308
Removed:
(create-fertilizer-product! "Phosphorus blend" "High-P" (hash n 50 p 200 k 50))
309
Removed:
(create-fertilizer-product! "Potassium blend" "High-K" (hash n 50 p 50 k 200))
305
Added:
;; Available fertilizer products
306
Added:
(create-fertilizer-product! "" "Balanced" (hash n 100 p 100 k 100))
307
Added:
(create-fertilizer-product! "Nitrogen blend" "High-N" (hash n 200 p 50 k 50))
308
Added:
(create-fertilizer-product! "Phosphorus blend" "High-P" (hash n 50 p 200 k 50))
309
Added:
(create-fertilizer-product! "Potassium blend" "High-K" (hash n 50 p 50 k 200))
310
310
311
Removed:
(define recipe (find-ferti-recipe test-date))
311
Added:
(define recipe (find-ferti-recipe test-date))
312
312
313
Removed:
(check-equal? (hash-count recipe) 9 "Recipe should have 9 fertilizers")
313
Added:
(check-equal? (hash-count recipe) 9 "Recipe should have 9 fertilizers")
314
314
315
Removed:
;; Verify solution is non-negative
316
Removed:
(for ([(fertilizer quantity) (in-hash recipe)])
317
Removed:
(check-true (>= quantity 0)
318
Removed:
(format "~a quantity must be non-negative"
319
Removed:
(fertilizer-product-name fertilizer))))))))
315
Added:
;; Verify solution is non-negative
316
Added:
(for ([(fertilizer quantity) (in-hash recipe)])
317
Added:
(check-true (>= quantity 0)
318
Added:
(format "~a quantity must be non-negative"
319
Added:
(fertilizer-product-name fertilizer))))))))