Prolog restrictions - puzzle - prolog

I'm developing a game named woodntDie. This game consists in 9 pieces which should look like this:
The aim is to arrange all those pieces and make it look like a dice:
This is the four final possible solutions with all those 9 pieces.
I'm trying to implement some restrictions but those arent enough:
sum([V5, V6, V11, V12, V17, V18, V23, V24, V29, V30, V35, V36, V41, V42, V47, V48, V53, V54], #=, 7),
The sum of the opposite faces should always be 7. In this restriction I'm telling that the sum of the top and bottom face it's 7.
sum([V2, V19, V7, V16, V31, V26], #=, 7),
The sum of the front and back faces is 7 aswell.
sum([V1, V49, V13, V8, V37, V25], #=, 7),
The sum of the left and right face is 7.
sum(Vars, #=, 21),
The sum of all the dots it's 21.
But I'm getting a really weird output, I'm getting repeated pieces and a load of solutions that doesnt make any sense and dont form a dice at all. I think I need few more restrictions and my questions are:
How do I make a restrictions saying that some pieces cant be repeated (notice that I can have two F pieces but all the other ones cant be similar)
How do I make a new restriction with this:
The only other remaining bar to have two spots on its side is C, so C must combine with A to make the 4 or 5 face
The only other bar to have a spot on its side near one end is E, so E must combine with B to make the 2 or 3 face.
I mean, is there any way to say "the sum of VX, VY, VZ it's 2 or 3".
And do you have any other opinion that would make the solution easier?
I'm struggling here and I really dont know what to do, I'd really appreciate all the help I could get.
Here's the whole code:
:-use_module(library(clpfd)).
:-use_module(library(lists)).
dice(Vars):-
Vars=[V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18,
V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30, V31, V32, V33, V34, V35, V36,
V37, V38, V39, V40, V41, V42, V43, V44, V45, V46, V47, V48, V49, V50, V51, V52, V53, V54],
% pecas dos topos
domain([V5, V6, V11, V12, V17, V18, V23, V24, V29, V30, V35, V36, V41, V42, V47, V48, V53, V54], 0, 1),
% pecas laterais
domain([V1, V2, V3, V4, V7, V8, V9, V10, V13, V14, V15, V16, V19, V20, V21, V22, V25, V26, V27, V28,
V31, V32, V33, V34, V37, V38, V39, V40, V43, V44, V45, V46, V49, V50, V51, V52], 0,2),
table([
[V1, V2, V3, V4, V5, V6], %%peca1
[V7, V8, V9, V10, V11, V12], %%peca2
[V13, V14, V15, V16, V17, V18], %%peca3
[V19, V20, V21, V22, V23, V24], %%peca4
[V25, V26, V27, V28, V29, V30], %%peca5
[V31, V32, V33, V34, V35, V36], %%peca6
[V37, V38, V39, V40, V41, V42], %%peca7
[V43, V44, V45, V46, V47, V48], %%peca8
[V49, V50, V51, V52, V53, V54]], %%peca9
[[2,2,0,0,1,0], %%peca1
[2,1,0,0,1,1], %%peca2
[2,0,0,0,1,1], %%peca3
[2,0,0,0,0,0], %%peca4
[1,0,0,0,1,0], %%peca5
[1,0,0,0,0,0], %%peca6
[1,0,0,0,0,0], %%peca7
[0,0,0,0,1,0], %%peca8
[0,0,0,0,0,0]]), %%peca9
% restricoes para as faces que envolve os topos de cada peça
% Na figura de descriçºao das peças os topos de cima estão guardados
% na posicao 5 de cada lista. Os topos de baixo estoa guardados na
% posicao 6 de cada lista
% Restrições quanto à soma de faces opostas(=7)
V2 + V19 + V7 #= TotalF1,
V8 + V37 + V25 #= TotalF2,
V5 + V23 + V11 + V53 + V47 + V41 + V29 + V35 + V17 #= TotalF3,
V18 + V36 + V30 + V54 + V48 + V42 + V6 + V24 + V12 #= TotalF4,
V1 + V49 + V13 #= TotalF5,
V16+ V31 + V26 #= TotalF6,
Totals = [TotalF1, TotalF2, TotalF3, TotalF4, TotalF5, TotalF6],
domain(Totals, 1,6),
all_different(Totals),
TotalF3 + TotalF4 #= 7,
TotalF1 + TotalF6 #= 7,
TotalF5 + TotalF2 #= 7,
A #= 100000*V1 + 10000*V2+ 1000*V5,
element(P1,Vars,A),
B #= 100000*V7 + 10000*V8 + 10*V11 + V12,
element(P2,Vars,B),
C #= 100000*V13 + 10*V17 + V18,
element(P3,Vars,C),
D #= 100000*V19,
element(P4,Vars,D),
E #= 100000*V25 + 10*V29,
element(P5,Vars,E),
F #= 100000*V31,
element(P6,Vars,F),
G #= 100000*V37,
element(P7,Vars,G),
H #= 100*V47,
element(P8,Vars,H),
I #= 0,
element(P9,Vars,I),
F#=P7,
F#=G,
PiecesIndex = [P1, P2, P3, P4, P5, P6, P8, P9],
all_different(PiecesIndex),
%There are 21 spots on the pieces
%sum(Vars, #=, 21),
labeling([],Vars),
show(Vars).
show([V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18,
V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30, V31, V32, V33, V34, V35, V36,
V37, V38, V39, V40, V41, V42, V43, V44, V45, V46, V47, V48, V49, V50, V51, V52, V53, V54]) :-
write([V1, V2, V3, V4, V5, V6]), nl,
write([V7, V8, V9, V10, V11, V12]), nl,
write([V13, V14, V15, V16, V17, V18]), nl,
write([V19, V20, V21, V22, V23, V24]), nl,
write([V25, V26, V27, V28, V29, V30]), nl,
write([V31, V32, V33, V34, V35, V36]), nl,
write([V37, V38, V39, V40, V41, V42]), nl,
write([V43, V44, V45, V46, V47, V48]), nl,
write([V49, V50, V51, V52, V53, V54]), nl.
and here's a pic with the representation of the dice and all those V's (each V represents the dots on each face)
ALL THE INFORMATION ABOUT THE GAME
Thank you so much in advance
Any doubt just ask or add me on skype: preserverance
Best regards

I mean, is there any way to say "the sum of VX, VY, VZ it's 2 or 3".
If this is the crucial question, try this:
(sum([VX,VY,VZ],#=,2);sum([VX,VY,VZ],#=,3))

Related

Graphviz - Vertical order of leaf nodes

I'm trying to generate a Tree graph (left to right one), to illustrate an object reference tree.
Beside this, I want the leafs to appear vertically consequent, because this indicates some order of execution.
The dot code is as follows:
digraph G {
rankdir=LR
subgraph cluster_0 {
L1 L2 L3 L4 L5 L6
L10 L20 L30 L40 L50 L60
{ rank=same;
L1 -> L2 -> L3 -> L4 -> L5 -> L6 ->
L10 -> L20 -> L30 -> L40 -> L50 -> L60
}
}
subgraph cluster_1 {
R
D10 D11
D20 D21 D22 D23
D30 D31 D32 D33 D34 D35 D36 D37
}
R->D10
R->D11
D10 -> D20
D10 -> D21
D11 -> D22
D11 -> D23
D20 -> D30
D20 -> D31
D21 -> D32
D21 -> D33
D22 -> D34
D22 -> D35
D23 -> D36
D23 -> D37
D30 -> L1
D20 -> L2
D31 -> L3
D32 -> L4
D21 -> L5
D33 -> L6
D34 -> L10
D22 -> L20
D35 -> L30
D36 -> L40
D23 -> L50
D37 -> L60
}
What I've got far (using https://dreampuf.github.io/GraphvizOnline/ to render SVG):
The problem is, I want the leafs to show up in the order of declaration.
Following some posts on SO, I've been playing around with constraint / invisible edges / ranking system
but couldn't quite get my hands on it.
It'd be nice if some graphvizard could extend their hand over here.
note that this procedure will be later automated with python, so some robust idea will be more than appreciated.
note 2, the tree could have up to hundreds of leafs.
note 3, if you know some good python lib to generate Graphs which is more intuitive (for you at least) than Graphviz, please comment. I'm currently using pydot & graphviz (py3)
By moving the leaf nodes out and adding weights to them I have the following:
I changed the dot input to:
digraph G {
rankdir=LR
subgraph cluster_0 {
L1 L2 L3 L4 L5 L6
L10 L20 L30 L40 L50 L60
}
subgraph cluster_1 {
R
D10 D11
D20 D21 D22 D23
D30 D31 D32 D33 D34 D35 D36 D37
}
R->D10
R->D11
D10 -> D20
D10 -> D21
D11 -> D22
D11 -> D23
D20 -> D30
D20 -> D31
D21 -> D32
D21 -> D33
D22 -> D34
D22 -> D35
D23 -> D36
D23 -> D37
D30 -> L1
D20 -> L2
D31 -> L3
D32 -> L4
D21 -> L5
D33 -> L6
D34 -> L10
D22 -> L20
D35 -> L30
D36 -> L40
D23 -> L50
D37 -> L60
{ rank=same;
L1-> L2 [style=invis, weight=1000]
L2-> L3 [style=invis, weight=1100]
L3-> L4 [style=invis, weight=1200]
L4-> L5 [style=invis, weight=1300]
L5-> L6 [style=invis, weight=1400]
L6-> L10 [style=invis, weight=1500]
L10-> L20 [style=invis, weight=1600]
L20-> L30 [style=invis, weight=1700]
L30-> L40 [style=invis, weight=1800]
L40-> L50 [style=invis, weight=1900]
L50-> L60 [style=invis, weight=2000]
}
}

Cobalt segmentation fault in blitter

From today, the Cobalt application that uses blitter/directFB layer does crash while starting with the below segmentation fault.
It seems to be linked with animated images but even by disabling it with --disable_image_animations the crash still occurs.
The same issue occurs with qual-e.appspot.com/webp.html and qual-e.appspot.com/awebp.html
Do you know how to solve the issue? In the meantime is it possible to disable animated images?
Caught signal: SIGSEGV (11)
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f62de]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f5e74]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f5e74]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f5e74]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f7b88]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f5e74]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f66f0]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f5e74]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f7b88]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f5e74]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f5e74]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f5e74]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f5e74]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f7b88]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f5e74]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f5e74]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f5e74]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f7b88]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f66f0]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f5e74]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f7b88]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f5e74]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f5e74]
cobalt::renderer::rasterizer::blitter::RenderTreeNodeVisitor::Visit()
[0x12f5e74]
cobalt::renderer::rasterizer::blitter::HardwareRasterizer::Impl::Submit()
[0x12f4d58]
cobalt::renderer::Pipeline::RasterizeSubmissionToRenderTarget()
[0x1096350] cobalt::renderer::Pipeline::RasterizeCurrentTree()
[0x1097630] base::Timer::RunScheduledTask() [0x4a5cf8]
MessageLoop::RunTask() [0x479d24]
MessageLoop::DeferOrRunPendingTask() [0x47aacc]
MessageLoop::DoDelayedWork() [0x47b83c]
base::MessagePumpDefault::Run() [0x47f188] base::RunLoop::Run()
[0x48ae80] MessageLoop::Run() [0x4794cc] base::Thread::ThreadMain()
[0x4a44ec] base::(anonymous namespace)::ThreadFunc() [0x4a38b8]
(anonymous namespace)::ThreadFunc() [0x4bb670] start_thread
[0x77f73e14] Aborted
It looks like an issue with WebP support.
Please file a bug here w/ your Cobalt version, build type, Starboard, stracktrace information.
https://issuetracker.google.com/issues/new?component=181120&template=699202

Where am I going wrong with my bounding box function?

I am writing a bounding box module for the octree library. You can find my branch here. In the function below, I try to make explicit the implicit bounding boxes of an Octree. The problem is, not all bounding boxes are valid.
This is the function in question, followed by a way to replicate the problem in ghci.
explicateMBB :: (BBox3, Octree a) -> [BBox3]
explicateMBB (mbb, (Leaf _)) = [mbb]
explicateMBB (mbb, (Node { split = split',
nwu = nwu',
nwd = nwd',
neu = neu',
ned = ned',
swu = swu',
swd = swd',
seu = seu',
sed = sed'
})) =
mbb:concatMap explicateMBB octList
where
octList = zip boxList children
boxList = [swdBox, sedBox, nwdBox, nedBox, swuBox, seuBox, nwuBox, neuBox]
children = [swd',sed',nwd',ned',swu',seu',nwu',neu']
swdBox = bound_corners swdCorner neuCorner
where
swdCorner = Vector3 (minX mbb) (minY mbb) (minZ mbb)
neuCorner = Vector3 (v3x split') (v3y split') (v3z split')
sedBox = bound_corners swdCorner neuCorner
where
swdCorner = Vector3 (v3x split') (minY mbb) (minZ mbb)
neuCorner = Vector3 (maxX mbb) (v3y split') (minZ mbb)
nwdBox = bound_corners swdCorner neuCorner
where
swdCorner = Vector3 (minX mbb) (v3y split') (minZ mbb)
neuCorner = Vector3 (v3x split') (maxY mbb) (v3z split')
nedBox = bound_corners swdCorner neuCorner
where
swdCorner = Vector3 (v3x split') (v3y split') (minZ mbb)
neuCorner = Vector3 (maxX mbb) (maxY mbb) (v3z split')
swuBox = bound_corners swdCorner neuCorner
where
swdCorner = Vector3 (minX mbb) (minY mbb) (v3z split')
neuCorner = Vector3 (v3x split') (v3y split') (maxZ mbb)
seuBox = bound_corners swdCorner neuCorner
where
swdCorner = Vector3 (v3x split') (minY mbb) (v3z split')
neuCorner = Vector3 (maxX mbb) (v3y split') (maxZ mbb)
nwuBox = bound_corners swdCorner neuCorner
where
swdCorner = Vector3 (minX mbb) (v3y split') (v3z split')
neuCorner = Vector3 (v3x split') (maxY mbb) (maxZ mbb)
neuBox = bound_corners swdCorner neuCorner
where
swdCorner = Vector3 (v3x split') (v3y split') (v3z split')
neuCorner = Vector3 (maxX mbb) (maxY mbb) (maxZ mbb)
To replicate problem:
git clone https://github.com/mlitchard/octree.git
git checkout MBB
stack ghci
In ghci, do the following :
:m + Data.List Data.Vector.Class System.Random System.Random.Shuffle Data.BoundingBox.B3
let infinity = (read "Infinity") :: Double
let swdCorner = Vector3 (-infinity) (-infinity) (-infinity)
let neuCorner = Vector3 (infinity) (infinity) (infinity)
let rbb = bound_corners swdCorner neuCorner
xGen <- getStdGen
yGen <- newStdGen
zGen <- newStdGen
let xPoints = shuffle' [-256 .. 256] 513 xGen
let yPoints = shuffle' [-256 .. 256] 513 yGen
let zPoints = shuffle' [-256 .. 256] 513 zGen
let xPoints' = map fromInteger xPoints :: [Double]
let yPoints' = map fromInteger yPoints :: [Double]
let zPoints' = map fromInteger zPoints :: [Double]
let tup513 = zip3 xPoints' yPoints' zPoints'
let construct_vect = (\(x,y,z) -> Vector3 x y z)
let vect513 = map construct_vect tup513
let pre_oct513 = zip vect513 [1 .. 513]
let octree513 = fromList pre_oct513
length $ filter (== False) $ map isValidMBB $ explicateMBB (rbb,octree513)
The answer will be 9, but should be 0.
I feel like one of the fooBox where clauses is wrong, but I have gone over each one several times, and I am not seeing which one it is.
If you needed a visual aid like I did, I found this pic to be helpful. My sample does 2 subdivisions.
Any insight into what is going wrong would be appreciated.
It's your second where clause:
where
swdCorner = Vector3 (v3x split') (minY mbb) (minZ mbb)
neuCorner = Vector3 (maxX mbb) (v3y split') (minZ mbb)
should be:
where
swdCorner = Vector3 (v3x split') (minY mbb) (minZ mbb)
neuCorner = Vector3 (maxX mbb) (v3y split') (v3z split') <-- v3z split'

Different variables - prolog puzzle solver

I'm developing a solution based on prolog restrictions.
Basically I got to rearrange 9 pieces with dots in it to make a dice. I have 9 pieces and 7 are all different but 2 are equal. I want to make a restriction that says it and I got this:
A #= 100000*V1 + 10000*V2+ 1000*V5,
element(P1,Vars,A),
B #= 100000*V7 + 10000*V8 + 10*V11 + V12,
element(P2,Vars,B),
C #= 100000*V13 + 10*V17 + V18,
element(P3,Vars,C),
D #= 100000*V19,
element(P4,Vars,D),
E #= 100000*V25 + 10*V29,
element(P5,Vars,E),
F #= 100000*V31,
element(P6,Vars,F),
G #= 100000*V37,
element(P7,Vars,G),
H #= 100*V47,
element(P8,Vars,H),
I #= 0,
element(P9,Vars,I),
F#=P7,
F#=G,
PiecesIndex = [P1, P2, P3, P4, P5, P6, P8, P9],
all_different(PiecesIndex),
Each VXY is one face of the piece and it can have 0,1,2 dots as you can see in the code below.
The Help I need is:
- Make sure that all pieces are different
- Make sure F and G are equal
- Make sure I dont use repeated pieces.
Can anyone help me! I've been here whole night trying to figure this out and I'm going nuts!
full code:
:-use_module(library(clpfd)).
:-use_module(library(lists)).
dice(Vars):-
Vars=[V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18,
V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30, V31, V32, V33, V34, V35, V36,
V37, V38, V39, V40, V41, V42, V43, V44, V45, V46, V47, V48, V49, V50, V51, V52, V53, V54],
% pecas dos topos
domain([V5, V6, V11, V12, V17, V18, V23, V24, V29, V30, V35, V36, V41, V42, V47, V48, V53, V54], 0, 1),
% pecas laterais
domain([V1, V2, V3, V4, V7, V8, V9, V10, V13, V14, V15, V16, V19, V20, V21, V22, V25, V26, V27, V28,
V31, V32, V33, V34, V37, V38, V39, V40, V43, V44, V45, V46, V49, V50, V51, V52], 0,2),
table([
[V1, V2, V3, V4, V5, V6], %%peca1
[V7, V8, V9, V10, V11, V12], %%peca2
[V13, V14, V15, V16, V17, V18], %%peca3
[V19, V20, V21, V22, V23, V24], %%peca4
[V25, V26, V27, V28, V29, V30], %%peca5
[V31, V32, V33, V34, V35, V36], %%peca6
[V37, V38, V39, V40, V41, V42], %%peca7
[V43, V44, V45, V46, V47, V48], %%peca8
[V49, V50, V51, V52, V53, V54]], %%peca9
[[2,2,0,0,1,0], %%peca1
[2,1,0,0,1,1], %%peca2
[2,0,0,0,1,1], %%peca3
[2,0,0,0,0,0], %%peca4
[1,0,0,0,1,0], %%peca5
[1,0,0,0,0,0], %%peca6
[1,0,0,0,0,0], %%peca7
[0,0,0,0,1,0], %%peca8
[0,0,0,0,0,0]]), %%peca9
% restricoes para as faces que envolve os topos de cada peça
% Na figura de descriçºao das peças os topos de cima estão guardados
% na posicao 5 de cada lista. Os topos de baixo estoa guardados na
% posicao 6 de cada lista
% Restrições quanto à soma de faces opostas(=7)
V2+V19+V7 #= TotalF1,
V8+V37+V25 #= TotalF2,
V5+V23+V11+V53+V47+V41+V29+V35+V17 #= TotalF3,
V18+V36+V30+V54+V48+V42+V6+V24+V12 #= TotalF4,
V1+V49+V13 #= TotalF5,
V16+V31+V26 #= TotalF6,
Totals = [TotalF1, TotalF2, TotalF3, TotalF4, TotalF5, TotalF6],
domain(Totals, 1,6),
all_different(Totals),
TotalF3 + TotalF4 #= 7,
TotalF1 + TotalF6 #= 7,
TotalF5 + TotalF2 #= 7,
A #= 100000*V1 + 10000*V2+ 1000*V5,
element(P1,Vars,A),
B #= 100000*V7 + 10000*V8 + 10*V11 + V12,
element(P2,Vars,B),
C #= 100000*V13 + 10*V17 + V18,
element(P3,Vars,C),
D #= 100000*V19,
element(P4,Vars,D),
E #= 100000*V25 + 10*V29,
element(P5,Vars,E),
F #= 100000*V31,
element(P6,Vars,F),
G #= 100000*V37,
element(P7,Vars,G),
H #= 100*V47,
element(P8,Vars,H),
I #= 0,
element(P9,Vars,I),
F#=P7,
F#=G,
PiecesIndex = [P1, P2, P3, P4, P5, P6, P8, P9],
all_different(PiecesIndex),
%There are 21 spots on the pieces
%sum(Vars, #=, 21),
labeling([],Vars),
show(Vars).
show([V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18,
V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30, V31, V32, V33, V34, V35, V36,
V37, V38, V39, V40, V41, V42, V43, V44, V45, V46, V47, V48, V49, V50, V51, V52, V53, V54]) :-
write([V1, V2, V3, V4, V5, V6]), nl,
write([V7, V8, V9, V10, V11, V12]), nl,
write([V13, V14, V15, V16, V17, V18]), nl,
write([V19, V20, V21, V22, V23, V24]), nl,
write([V25, V26, V27, V28, V29, V30]), nl,
write([V31, V32, V33, V34, V35, V36]), nl,
write([V37, V38, V39, V40, V41, V42]), nl,
write([V43, V44, V45, V46, V47, V48]), nl,
write([V49, V50, V51, V52, V53, V54]), nl.
Best regards

F# - Sort a matrix containing tuples

I do not find a way to sort the values included in the columns of the following matrix of tuples :
Matrix<float * float> =
matrix [[(1.0, 145.0); (1.0, 45.0); (1.0, 130.0); (1.0, 30.0); (1.0, 130.0)]
[(2.0, 45.0); (2.0, 45.0); (2.0, 30.0); (2.0, 30.0); (2.0, 30.0)]
[(3.0, 130.0); (3.0, 30.0); (3.0, 145.0); (3.0, 45.0); (3.0, 130.0)]
[(4.0, 30.0); (4.0, 30.0); (4.0, 45.0); (4.0, 45.0); (4.0, 30.0)]
[(5.0, 130.0); (5.0, 30.0); (5.0, 130.0); (5.0, 30.0); (5.0, 145.0)]]
I would like to sort each column depending on the second element of the tuple. For example here the answer would be :
matrix [[(1.0, 145.0); (1.0, 45.0); (3.0, 145.0); (3.0, 45.0); (5.0, 145.0)]
[(3.0, 130.0); (2.0, 45.0); (1.0, 130.0); (4.0, 45.0); (1.0, 130.0)]
[(5.0, 130.0); (3.0, 30.0); (5.0, 130.0); (1.0, 30.0); (3.0, 130.0)]
[(2.0, 45.0); (4.0, 30.0); (4.0, 45.0); (2.0, 30.0); (2.0, 30.0)]
[(4.0, 30.0); (5.0, 30.0); (2.0, 30.0); (5.0, 30.0); (4.0, 30.0)]]
Thank you in advance !
In my experience, when working with arrays (2D and/or matrix) I found that working with arrays internally is often the fastest way to go.
For example, combining Daniel's and Ankur's approaches in a mutable way:
let mutableSortByCol f (m:Matrix<'T>) =
let columns = [| for c in 0 .. m.NumCols - 1 ->
m.Column c |> Vector.Generic.toArray |]
for c in 0 .. m.NumCols - 1 do
columns.[c] |> Array.sortInPlaceBy f
Matrix.Generic.init (m.NumRows) (m.NumCols) (fun r c -> columns.[c].[r])
I converted the matrix to an array of columns ('a[][], not 'a[,]), and performed an in-place sort on each column. After that, I filled a new matrix with the sorted result. Note that the original matrix remains unmodified: the columns array is populated with copies of the column vectors (Vector.toArray creates a new array).
This approach is faster because it needs no transposes, sorts columns in place, and needs no conversion to and from intermediate list structures by keeping everything array-oriented. I suspect it could be made even faster if the Matrix module supported conversion to/from 'a[][] as well, although it's perhaps not really suited for matrices.
Also, in case you didn't know: you can make use of F#'s structural comparison of tuples to sort by second element descending, first element ascending:
Example:
> mutableSortByCol (fun (a,b) -> (-b,a)) M;;
val it : Matrix<float * float> =
matrix [[(1.0, 145.0); (1.0, 45.0); (3.0, 145.0); (3.0, 45.0); (5.0, 145.0)]
[(3.0, 130.0); (2.0, 45.0); (1.0, 130.0); (4.0, 45.0); (1.0, 130.0)]
[(5.0, 130.0); (3.0, 30.0); (5.0, 130.0); (1.0, 30.0); (3.0, 130.0)]
[(2.0, 45.0); (4.0, 30.0); (4.0, 45.0); (2.0, 30.0); (2.0, 30.0)]
[(4.0, 30.0); (5.0, 30.0); (2.0, 30.0); (5.0, 30.0); (4.0, 30.0)]]
Below is such a function:
let sortByCol f (m:Matrix<'T>) =
let n = m.Transpose
[for i = 0 to n.NumRows-1 do
yield [for j in n.Row(i) -> j]
|> List.sortBy f ]
|> Matrix.Generic.ofList
|> Matrix.Generic.transpose
Usage as particular to this question:
matrix |> sortByCol (fun (_,b) -> -b)
UPDATED: To make the sort by col function generic.
I've never used Matrix before so there might be a better way, but this seems to work:
let sortMatrix (m:Matrix<_>) =
seq {
for c in 0 .. (m.NumCols - 1) do
let arr = [| for r in 0 .. (m.NumRows - 1) -> m.[r, c] |]
arr |> Array.sortInPlaceBy (fun (_, b) -> -b : float) //(snd >> (~-))
yield arr
}
|> Matrix.Generic.ofSeq
|> Matrix.Generic.transpose

Resources